2017-03-07 63 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