PyQt4 "mpl_connect" 构造无法通过 类
PyQt4 "mpl_connect" construct doesn't work through classes
我是这个 Whosebug 社区的新用户。我正在挣扎
从我的代码中出现一些我无法真正弄清楚的问题开始。
在我的以下代码中,我试图为我的 GUI 定义一个自定义选择器工具。
我已经复制了 "bug" 和我的程序的主要结构)。
我正在尝试将 matplotlib 信号与 "mpl_connect" 构造函数连接起来,但不幸的是
单击、释放和鼠标移动的信号以及相关方法似乎是
canvas class 无效。这是不起作用的代码。
import sys
from PyQt4 import QtGui,QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Example, self).__init__(parent)
self.setupUi(self)
def setupUi(self,parent):
self.canvas=MyCanvas(self)
self.toolbar=MyToolBar(self.canvas,self)
self.addToolBar(QtCore.Qt.BottomToolBarArea,parent.toolbar)
class MyCanvas(FigureCanvas):
def __init__(self,parent):
self.fig=Figure()
FigureCanvas.__init__(self, self.fig)
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
self.setParent(parent)
class MyToolBar(QtGui.QToolBar):
def __init__(self,canvas,parent):
super(MyToolBar,self).__init__(parent)
#
self.PICK_act=QtGui.QAction("PUSH ME!!",parent,checkable=True)
self.PICK_act.toggled.connect(lambda: self.pickData(canvas))
#
self.addAction(self.PICK_act)
self.addSeparator()
## Creating the matplotlib toolbar
self.mpl_tool=NavigationToolbar(canvas,parent)
## Merge the two toolbar
self.addWidget(self.mpl_tool)
def pickData(self,canvas):
P=Picker(canvas)
if self.PICK_act.isChecked():
print "CHECKED"
P._activation(True)
else:
print "NOT CHECKED"
P._activation(False)
class Picker(object):
def __init__(self,canvas):
self.index=None
self.is_pressed=None
# To define the event of PickData_ACTION
self.canvas=canvas
###
self.selPressEvent=None
self.selReleaseEvent=None
self.selMoveEvent=None
def _activation(self,condition):
if condition==True:
self.selPressEvent=self.canvas.mpl_connect('button_press_event',self.onpress)
self.selReleaseEvent=self.canvas.mpl_connect('button_release_event',self.onrelease)
self.selMoveEvent=self.canvas.mpl_connect('motion_notify_event',self.onmotion)
print "Picker ON"
return True
else:
self.canvas.mpl_disconnect(self.selPressEvent)
self.canvas.mpl_disconnect(self.selReleaseEvent)
self.canvas.mpl_disconnect(self.selMoveEvent)
print "Picker OFF"
return False
def onpress(self,event):
print "..clicked"
self.x0 = event.xdata
self.y0 = event.ydata
self.is_pressed=True
def onrelease(self,event):
print "...released"
self.x1 = event.xdata
self.y1 = event.ydata
self.is_pressed=False
def onmotion(self,event):
if self.is_pressed==True:
print "moving"
# =========================================================================
def run():
App=QtGui.QApplication(sys.argv)
GUI=Example()
GUI.show()
sys.exit(App.exec_())
#
run()
如果我在 "MyCanvas" class 中定义相同的方法,它们就会起作用。
工作代码是这样的:
import sys
from PyQt4 import QtGui,QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Example, self).__init__(parent)
self.setupUi(self)
def setupUi(self,parent):
self.canvas=MyCanvas(self)
self.toolbar=MyToolBar(self.canvas,self)
self.addToolBar(QtCore.Qt.BottomToolBarArea,parent.toolbar)
class MyCanvas(FigureCanvas):
def __init__(self,parent):
self.fig=Figure()
FigureCanvas.__init__(self, self.fig)
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
self.setParent(parent)
###
self.selPressEvent=None
self.selReleaseEvent=None
self.selMoveEvent=None
def onpress(self,event):
print "MyCanvas::onpress ---> clicked"
def onrelease(self,event):
print "MyCanvas::onrelease ---> release"
def onmotion(self,event):
print "MyCanvas::onmotion ---> motion"
class MyToolBar(QtGui.QToolBar):
def __init__(self,canvas,parent):
super(MyToolBar,self).__init__(parent)
#
self.PICK_act=QtGui.QAction("PUSH ME!!",parent,checkable=True)
self.PICK_act.toggled.connect(lambda: self.pickData(canvas))
#
self.addAction(self.PICK_act)
self.addSeparator()
## Creating the matplotlib toolbar
self.mpl_tool=NavigationToolbar(canvas,parent)
## Merge the two toolbar
self.addWidget(self.mpl_tool)
def pickData(self,canvas):
P=Picker(canvas)
if self.PICK_act.isChecked():
print "CHECKED"
P._activation(True)
else:
print "NOT CHECKED"
P._activation(False)
class Picker(object):
def __init__(self,canvas):
self.index=None
self.is_pressed=None
# To define the event of PickData_ACTION
self.canvas=canvas
def _activation(self,condition):
if condition==True:
self.canvas.selPressEvent=self.canvas.mpl_connect('button_press_event',self.canvas.onpress)
self.canvas.selReleaseEvent=self.canvas.mpl_connect('button_release_event',self.canvas.onrelease)
self.canvas.selMoveEvent=self.canvas.mpl_connect('motion_notify_event',self.canvas.onmotion)
print "Picker ON"
return True
else:
self.canvas.mpl_disconnect(self.canvas.selPressEvent)
self.canvas.mpl_disconnect(self.canvas.selReleaseEvent)
self.canvas.mpl_disconnect(self.canvas.selMoveEvent)
print "Picker OFF"
return False
def onpress(self,event):
print "..clicked"
self.x0 = event.xdata
self.y0 = event.ydata
self.is_pressed=True
def onrelease(self,event):
print "...released"
self.x1 = event.xdata
self.y1 = event.ydata
self.is_pressed=False
def onmotion(self,event):
if self.is_pressed==True:
print "moving"
# =========================================================================
def run():
App=QtGui.QApplication(sys.argv)
GUI=Example()
GUI.show()
sys.exit(App.exec_())
#
run()
我需要将 "Picker" 和相关方法与 "MyCanvas" 分开
class 为了清楚起见。但我真的不明白什么是
我的第一个代码出错了:信号似乎已发出但未收到。
STDERR 中没有错误报告。我想我正确地尊重了 classes/methods 的范围。
任何帮助,将不胜感激。非常感谢。
编辑解决方案:
对于可能涉及的人,我解决了初始化选择器的问题
"MyToolbar" class:
self.Picker=Picker(canvas)
def pickData(self,canvas):
if self.PICK_act.isChecked():
print "CHECKED"
self.Picker._activation(True)
else:
print "NOT CHECKED"
self.Picker._activation(False)
问题是 mpl 回调注册表只持有对它所传递的回调的弱引用(逻辑是,如果你不小心持有你从 mpl_connect
取回的令牌,你可能会结束处理你无法获得引用但永远不会被 gc 的对象,因为 mpl 持有硬引用)。所以发生的事情是你正在将一个方法从你的 Picker
对象连接到回调系统,它超出范围,得到 gc'd 然后当 mpl 转到 运行 回调时,它发现对象已经消失并自动删除已失效的函数。
您所要做的就是在工具栏 class.
中保持对 Picker
对象的引用
这应该在上游有更清楚的记录。
我是这个 Whosebug 社区的新用户。我正在挣扎 从我的代码中出现一些我无法真正弄清楚的问题开始。 在我的以下代码中,我试图为我的 GUI 定义一个自定义选择器工具。 我已经复制了 "bug" 和我的程序的主要结构)。 我正在尝试将 matplotlib 信号与 "mpl_connect" 构造函数连接起来,但不幸的是 单击、释放和鼠标移动的信号以及相关方法似乎是 canvas class 无效。这是不起作用的代码。
import sys
from PyQt4 import QtGui,QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Example, self).__init__(parent)
self.setupUi(self)
def setupUi(self,parent):
self.canvas=MyCanvas(self)
self.toolbar=MyToolBar(self.canvas,self)
self.addToolBar(QtCore.Qt.BottomToolBarArea,parent.toolbar)
class MyCanvas(FigureCanvas):
def __init__(self,parent):
self.fig=Figure()
FigureCanvas.__init__(self, self.fig)
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
self.setParent(parent)
class MyToolBar(QtGui.QToolBar):
def __init__(self,canvas,parent):
super(MyToolBar,self).__init__(parent)
#
self.PICK_act=QtGui.QAction("PUSH ME!!",parent,checkable=True)
self.PICK_act.toggled.connect(lambda: self.pickData(canvas))
#
self.addAction(self.PICK_act)
self.addSeparator()
## Creating the matplotlib toolbar
self.mpl_tool=NavigationToolbar(canvas,parent)
## Merge the two toolbar
self.addWidget(self.mpl_tool)
def pickData(self,canvas):
P=Picker(canvas)
if self.PICK_act.isChecked():
print "CHECKED"
P._activation(True)
else:
print "NOT CHECKED"
P._activation(False)
class Picker(object):
def __init__(self,canvas):
self.index=None
self.is_pressed=None
# To define the event of PickData_ACTION
self.canvas=canvas
###
self.selPressEvent=None
self.selReleaseEvent=None
self.selMoveEvent=None
def _activation(self,condition):
if condition==True:
self.selPressEvent=self.canvas.mpl_connect('button_press_event',self.onpress)
self.selReleaseEvent=self.canvas.mpl_connect('button_release_event',self.onrelease)
self.selMoveEvent=self.canvas.mpl_connect('motion_notify_event',self.onmotion)
print "Picker ON"
return True
else:
self.canvas.mpl_disconnect(self.selPressEvent)
self.canvas.mpl_disconnect(self.selReleaseEvent)
self.canvas.mpl_disconnect(self.selMoveEvent)
print "Picker OFF"
return False
def onpress(self,event):
print "..clicked"
self.x0 = event.xdata
self.y0 = event.ydata
self.is_pressed=True
def onrelease(self,event):
print "...released"
self.x1 = event.xdata
self.y1 = event.ydata
self.is_pressed=False
def onmotion(self,event):
if self.is_pressed==True:
print "moving"
# =========================================================================
def run():
App=QtGui.QApplication(sys.argv)
GUI=Example()
GUI.show()
sys.exit(App.exec_())
#
run()
如果我在 "MyCanvas" class 中定义相同的方法,它们就会起作用。 工作代码是这样的:
import sys
from PyQt4 import QtGui,QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Example, self).__init__(parent)
self.setupUi(self)
def setupUi(self,parent):
self.canvas=MyCanvas(self)
self.toolbar=MyToolBar(self.canvas,self)
self.addToolBar(QtCore.Qt.BottomToolBarArea,parent.toolbar)
class MyCanvas(FigureCanvas):
def __init__(self,parent):
self.fig=Figure()
FigureCanvas.__init__(self, self.fig)
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
self.setParent(parent)
###
self.selPressEvent=None
self.selReleaseEvent=None
self.selMoveEvent=None
def onpress(self,event):
print "MyCanvas::onpress ---> clicked"
def onrelease(self,event):
print "MyCanvas::onrelease ---> release"
def onmotion(self,event):
print "MyCanvas::onmotion ---> motion"
class MyToolBar(QtGui.QToolBar):
def __init__(self,canvas,parent):
super(MyToolBar,self).__init__(parent)
#
self.PICK_act=QtGui.QAction("PUSH ME!!",parent,checkable=True)
self.PICK_act.toggled.connect(lambda: self.pickData(canvas))
#
self.addAction(self.PICK_act)
self.addSeparator()
## Creating the matplotlib toolbar
self.mpl_tool=NavigationToolbar(canvas,parent)
## Merge the two toolbar
self.addWidget(self.mpl_tool)
def pickData(self,canvas):
P=Picker(canvas)
if self.PICK_act.isChecked():
print "CHECKED"
P._activation(True)
else:
print "NOT CHECKED"
P._activation(False)
class Picker(object):
def __init__(self,canvas):
self.index=None
self.is_pressed=None
# To define the event of PickData_ACTION
self.canvas=canvas
def _activation(self,condition):
if condition==True:
self.canvas.selPressEvent=self.canvas.mpl_connect('button_press_event',self.canvas.onpress)
self.canvas.selReleaseEvent=self.canvas.mpl_connect('button_release_event',self.canvas.onrelease)
self.canvas.selMoveEvent=self.canvas.mpl_connect('motion_notify_event',self.canvas.onmotion)
print "Picker ON"
return True
else:
self.canvas.mpl_disconnect(self.canvas.selPressEvent)
self.canvas.mpl_disconnect(self.canvas.selReleaseEvent)
self.canvas.mpl_disconnect(self.canvas.selMoveEvent)
print "Picker OFF"
return False
def onpress(self,event):
print "..clicked"
self.x0 = event.xdata
self.y0 = event.ydata
self.is_pressed=True
def onrelease(self,event):
print "...released"
self.x1 = event.xdata
self.y1 = event.ydata
self.is_pressed=False
def onmotion(self,event):
if self.is_pressed==True:
print "moving"
# =========================================================================
def run():
App=QtGui.QApplication(sys.argv)
GUI=Example()
GUI.show()
sys.exit(App.exec_())
#
run()
我需要将 "Picker" 和相关方法与 "MyCanvas" 分开 class 为了清楚起见。但我真的不明白什么是 我的第一个代码出错了:信号似乎已发出但未收到。 STDERR 中没有错误报告。我想我正确地尊重了 classes/methods 的范围。 任何帮助,将不胜感激。非常感谢。
编辑解决方案: 对于可能涉及的人,我解决了初始化选择器的问题 "MyToolbar" class:
self.Picker=Picker(canvas)
def pickData(self,canvas):
if self.PICK_act.isChecked():
print "CHECKED"
self.Picker._activation(True)
else:
print "NOT CHECKED"
self.Picker._activation(False)
问题是 mpl 回调注册表只持有对它所传递的回调的弱引用(逻辑是,如果你不小心持有你从 mpl_connect
取回的令牌,你可能会结束处理你无法获得引用但永远不会被 gc 的对象,因为 mpl 持有硬引用)。所以发生的事情是你正在将一个方法从你的 Picker
对象连接到回调系统,它超出范围,得到 gc'd 然后当 mpl 转到 运行 回调时,它发现对象已经消失并自动删除已失效的函数。
您所要做的就是在工具栏 class.
中保持对Picker
对象的引用
这应该在上游有更清楚的记录。