import os
import sys
import numpy as np
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine, qmlRegisterType
from PySide6.QtCore import QObject, Signal, Property
# 导入我们的自定义绘图项
from PlotItem import PlotItem
class PlotController(QObject):
"""用于QML和Python之间通信的控制器"""
def __init__(self, parent=None):
super(PlotController, self).__init__(parent)
self.plot_item = None
def set_plot_item(self, plot_item):
"""设置绘图项引用"""
self.plot_item = plot_item
# 初始化图表数据
x = np.linspace(0, 4 * np.pi, 1000)
y = np.sin(x)
self.update_plot(x, y)
def update_plot(self, x, y):
"""更新绘图数据"""
if self.plot_item:
self.plot_item.update_plot(x, y)
def resetView(self): # 添加这个方法
"""重置视图"""
if self.plot_item and hasattr(self.plot_item, 'canvas'):
self.plot_item.canvas.axes.set_xlim(0, 4 * np.pi)
self.plot_item.canvas.axes.set_ylim(-1.2, 1.2)
self.plot_item.canvas.draw()
self.plot_item.update()
def main():
app = QGuiApplication(sys.argv)
try:
# 注册自定义QML类型
print("Registering PlotItem...")
qmlRegisterType(PlotItem, "MatplotlibPlot", 1, 0, "PlotItem")
# 设置QML引擎
engine = QQmlApplicationEngine()
# 创建控制器实例
plot_controller = PlotController()
engine.rootContext().setContextProperty("plotController", plot_controller)
# 加载QML文件
qml_path = os.path.join(os.path.dirname(__file__), "Main.qml")
print(f"Loading QML from: {qml_path}")
engine.load(qml_path)
if not engine.rootObjects():
print("Error: Failed to load QML file!")
# 检查文件是否存在
if not os.path.exists(qml_path):
print(f"QML file not found at: {qml_path}")
sys.exit(-1)
root = engine.rootObjects()[0]
plot_item = root.findChild(QObject, "myPlotItem")
if plot_item:
plot_controller.set_plot_item(plot_item)
else:
print("Warning: Could not find PlotItem in QML")
sys.exit(app.exec())
except Exception as e:
print(f"Fatal error: {e}")
sys.exit(-1)
if __name__ == "__main__":
main()
以上是main.py文件import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PySide6.QtCore import QObject, Signal, Property, QRectF, Qt
from PySide6.QtGui import QColor, QPainter
from PySide6.QtQuick import QQuickPaintedItem
# 添加QML注册装饰器
QML_IMPORT_NAME = "MatplotlibPlot"
QML_IMPORT_MAJOR_VERSION = 1
class MatplotlibCanvas(FigureCanvas):
"""Matplotlib canvas to be used in QML"""
def __init__(self, parent=None, width=5, height=4, dpi=100):
# 创建图形和子图
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111)
# 初始化画布
super(MatplotlibCanvas, self).__init__(self.fig)
self.setParent(parent)
# 配置图形
self.axes.set_xlim(0, 4 * np.pi)
self.axes.set_ylim(-1.2, 1.2)
self.axes.set_xticks([0, np.pi, 2 * np.pi, 3 * np.pi, 4 * np.pi])
self.axes.set_xticklabels(['0', 'π', '2π', '3π', '4π'])
self.axes.grid(True, linestyle='--', alpha=0.6)
# 绘制初始图形
self.x = np.linspace(0, 4 * np.pi, 1000)
self.y = np.sin(self.x)
self.line, = self.axes.plot(self.x, self.y, color='blue', linewidth=2, label='y = sin(x)')
self.axes.legend(fontsize=12)
# 初始化矩形选择器
self.rectangle_selector = None
self.coord_text = self.axes.text(0.02, 0.95, '', transform=self.axes.transAxes,
bbox=dict(facecolor='white', alpha=0.8))
# 连接事件
self.fig.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
self.fig.canvas.mpl_connect('key_press_event', self.on_key)
self.draw()
def on_mouse_move(self, event):
if not event.inaxes:
self.coord_text.set_text('')
self.draw()
return
xdata, ydata = event.xdata, event.ydata
idx = np.argmin(np.abs(self.x - xdata))
if np.abs(ydata - self.y[idx]) < 0.1:
self.coord_text.set_text(f'x={self.x[idx]:.3f}\ny={self.y[idx]:.3f}')
else:
self.coord_text.set_text('')
self.draw()
def on_key(self, event):
if event.key == 'r':
self.axes.set_xlim(0, 4 * np.pi)
self.axes.set_ylim(-1.2, 1.2)
self.draw()
def update_data(self, x, y):
"""更新图表数据"""
self.x = x
self.y = y
self.line.set_data(x, y)
self.axes.relim()
self.axes.autoscale_view()
self.draw()
def on_select(self, eclick, erelease):
"""矩形选择回调函数"""
x1, y1 = eclick.xdata, eclick.ydata
x2, y2 = erelease.xdata, erelease.ydata
if None in (x1, x2, y1, y2):
return
self.axes.set_xlim(min(x1, x2), max(x1, x2))
self.axes.set_ylim(min(y1, y2), max(y1, y2))
self.draw()
def enable_rectangle_selector(self):
"""启用矩形选择器"""
if self.rectangle_selector is None:
from matplotlib.widgets import RectangleSelector
self.rectangle_selector = RectangleSelector(
self.axes, self.on_select, useblit=True,
button=[1], minspanx=5, minspany=5,
spancoords='pixels', interactive=True
)
else:
self.rectangle_selector.set_active(True)
class PlotItem(QQuickPaintedItem):
"""QML可使用的绘图项"""
def __init__(self, parent=None):
super(PlotItem, self).__init__(parent)
self.canvas = MatplotlibCanvas(width=4, height=3, dpi=100)
self.setRenderTarget(QQuickPaintedItem.FramebufferObject)
self.setAcceptedMouseButtons(Qt.AllButtons)
self.setAcceptHoverEvents(True)
def paint(self, painter):
"""绘制Matplotlib图表到QML"""
if self.canvas:
# 转换Matplotlib画布为QImage并绘制
self.canvas.resize(self.width(), self.height())
self.canvas.draw()
# 获取Matplotlib画布的图像
img = self.canvas.grab()
painter.drawImage(QRectF(0, 0, self.width(), self.height()), img)
def mousePressEvent(self, event):
"""处理鼠标按下事件"""
if self.canvas:
# 转换QML坐标到Matplotlib坐标
x = event.position().x()
y = event.position().y()
# 这里需要更复杂的坐标转换
# 简化处理,仅传递事件
self.canvas.enable_rectangle_selector()
super(PlotItem, self).mousePressEvent(event)
def keyPressEvent(self, event):
"""处理键盘事件"""
if self.canvas:
# 模拟Matplotlib键盘事件
self.canvas.on_key(type('KeyEvent', (object,), {
'key': event.text().lower()
})())
super(PlotItem, self).keyPressEvent(event)
def update_plot(self, x, y):
"""更新图表数据"""
if self.canvas:
self.canvas.update_data(x, y)
self.update()
最新发布