2017-03-07 44 views
3

我有一个可拖动的matplotlib对象库,我正尝试在PyQt5 GUI中使用。对象的event listeners在独立的matplotlib图形窗口中正确运行,但在图形嵌入到QT窗口小部件中时不起作用。当我试图拖动QT小部件中的补丁时,两个图均正确呈现,并且没有错误消息。matplotlib在PyQT Widget中不能正常工作的事件监听器

对象MCVE:

import matplotlib.patches as patches 

class _DragObj: 
    def __init__(self, ax): 
     self.parentcanvas = ax.figure.canvas 
     self.parentax = ax 

     self.clickpress = self.parentcanvas.mpl_connect('button_press_event', self.on_click) 
     self.clicked = False 

    def on_click(self, event): 
     if event.inaxes != self.parentax: return 

     self.mousemotion = self.parentcanvas.mpl_connect('motion_notify_event', self.on_motion) 
     self.clickrelease = self.parentcanvas.mpl_connect('button_release_event', self.on_release) 

     self.clickx = event.xdata 
     self.clicky = event.ydata 

     self.clicked = True 

    def on_release(self, event): 
     self.clicked = False 
     self.disconnect() 

    def disconnect(self): 
     self.parentcanvas.mpl_disconnect(self.mousemotion) 
     self.parentcanvas.mpl_disconnect(self.clickrelease) 
     self.parentcanvas.draw() 

    def stopdrag(self): 
     self.myobj.set_url('') 
     self.parentcanvas.mpl_disconnect(self.clickpress) 

class _DragPatch(_DragObj): 
    def __init__(self, ax, xy): 
     super().__init__(ax) 

     self.oldxy = xy 

    def on_motion(self, event): 
     if not self.clicked: return 
     if event.inaxes != self.parentax: return 

     oldx, oldy = self.oldxy 
     dx = event.xdata - self.clickx 
     dy = event.ydata - self.clicky 
     newxy = [oldx + dx, oldy + dy] 
     self.myobj.xy = newxy 

     self.parentcanvas.draw() 

    def on_release(self, event): 
     self.clicked = False 
     self.oldxy = self.myobj.xy 

     self.disconnect() 

class DragRectangle(_DragPatch): 
    def __init__(self, ax, xy, width, height, angle=0.0, **kwargs): 
     self.myobj = patches.Rectangle(xy, width, height, angle, **kwargs) 
     ax.add_artist(self.myobj) 

     super().__init__(ax, xy) 

运作matplotlib例如:

import minidrag 
import matplotlib.pyplot as plt 

fig = plt.figure() 
ax = fig.add_subplot(111) 
rect = minidrag.DragRectangle(ax, (0, 0), 2, 1) 

ax.set_xlim(-5, 5) 
ax.set_ylim(-5, 5) 

plt.show() 

的非功能性的PyQt例如:

import sys 
from PyQt5 import QtWidgets as QtW 
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas 
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar 
from matplotlib.figure import Figure 
import minidrag 

class windowGUI(QtW.QDialog): 
    def __init__(self): 
     super().__init__() 

     # Set up figure 
     width_px = 800 
     height_px = 600 

     rect = QtW.QDesktopWidget().availableGeometry() 
     screenrez = (rect.width(), rect.height()) 
     left_px = (screenrez[0] - width_px)/2 
     top_px = (screenrez[1] - height_px)/2 
     self.setGeometry(left_px, top_px, width_px, height_px) 
     self.canvas = PlotCanvas() 

     layout = QtW.QVBoxLayout() 
     layout.addWidget(NavigationToolbar(self.canvas, self)) 
     layout.addWidget(self.canvas) 
     self.setLayout(layout) 

class PlotCanvas(FigureCanvas): 
    def __init__(self): 
     fig = Figure(frameon=False) 
     super().__init__(fig) 
     super().setSizePolicy(QtW.QSizePolicy.Expanding, QtW.QSizePolicy.Expanding) 
     super().updateGeometry() 

     ax = fig.add_subplot(111) 
     rect = minidrag.DragRectangle(ax, (0, 0), 2, 1) 

     ax.set_xlim(-5, 5) 
     ax.set_ylim(-5, 5) 

app = QtW.QApplication(sys.argv) 
window = windowGUI() 
window.show() 
sys.exit(app.exec_()) 

我使用Python 3.6.0,matplotlib(2.0 .0)和PyQt5(5.8)

我错过了什么?

+0

您是否尝试过实施'高清dragEnterEvent(个体经营,事件): event.acceptProposedAction()'所有_embedding_'QWidget's?否则,事件不能传播到嵌入的小部件。顺便说一句,你可能需要为'dropEvent'做同样的事情。请参见[QWidget.dragEnterEvent](http://pyqt.sourceforge.net/Docs/PyQt4/qwidget.html#dragEnterEvent)。 –

+0

我不确定我是否理解。 matplotlib的标准事件功能(鼠标悬停坐标显示,平移/缩放等)正常运行,无需重新定义窗口小部件的事件。为什么这是不同的? – excaza

+0

因为AFAIK拖动事件默认被忽略。也许这足以设置['setAcceptDrops(True)'](http://pyqt.sourceforge.net/Docs/PyQt4/qwidget.html#setAcceptDrops)(默认情况下,至少相应的属性为False;请参见[ C++文档](http://doc.qt.io/qt-5/qwidget.html#acceptDrops-prop))。您可能还想阅读[C++拖放文档](http://doc.qt.io/qt-5/dnd.html)以获取有关如何在Qt中进行拖放的更多信息(pyqt参考有时是当涉及到更具体的主题时,有点稀疏)。 –

回答

4

原来,问题不是拖动事件,而是您的minidrag.DragRectangle实例只是被垃圾收集(而画布仍然显示在其原始位置的矩形)。

作为修复可以设置矩形作为一个实例变量:

self.rect = minidrag.DragRectangle(ax, (0, 0), 2, 1) 

(测试用Python2.7和PyQt4的)

+0

哦,这很烦人:) – excaza