2016-02-04 3 views
0

Я новый пользователь этого сообщества stackoverflow. Я борюсь от дней с некоторыми проблемами в моем коде, которые я не могу понять. В моем следующем коде я пытаюсь определить пользовательский инструмент селектора для моего графического интерфейса. Я воспроизвел «ошибку» и основную структуру моей программы). Я пытаюсь подключить сигнал matplotlib с помощью «mpl_connect» structor, но, к сожалению, сигнал нажатия кнопки, отпускания и движения мыши, а относительные методы, похоже, неактивны для класса холста. Вот неработающий код.Конструкция PyQt4 "mpl_connect" не работает через классы

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», они будут работать. Рабочий код заключается в следующем:

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» класса для ясности. Но я действительно не могу понять, что происходит в моем первом коде: не так. Сигнал кажется испущенным, но не получен. В STDERR не сообщается об ошибке. Я думаю, что я правильно отношусь к определению классов/методов. Любая помощь будет оценена по достоинству. Заранее большое спасибо.

EDIT РЕШЕНИЕ: Для кого это может касаться, я решил проблему инициализации сборщика в в «MyToolbar» класс:

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) 
+0

Для справок в будущем, чем меньше кода в вашем вопросе, тем лучше. Вам нужно только один из вызовов подключения, чтобы показать проблему и в рабочем случае, помещая панель инструментов на панели инструментов, а не вниз в 'Picker'. Чем проще понять ваш код, тем полезнее он будет для будущих читателей (поскольку они могут сказать, что у них такая же проблема) и с большей вероятностью получить ответ (поскольку люди будут более легко понимать, что вы делаете/пытаетесь сделать). – tacaswell

ответ

0

Проблема заключается в том, что реестр MPL обратного вызова содержит только слабые ссылки на обратные вызовы передаются (логика заключается в том, что если вы не будете осторожны в том, чтобы удерживать маркеры, вы возвращаетесь с mpl_connect, вы можете закончить тем, что у вас нет способа получить ссылку, но никогда не будет gc'd, потому что mpl держит твердую ссылку). Так что происходит то, что вы подключаете методы от вашего объекта Picker к системе обратного вызова, он выходит из области видимости, получает gc'd, а затем, когда mpl запускает обратные вызовы, обнаруживает, что объект пропал и автоматически удаляет несуществующие функции.

Все, что вам нужно сделать, это удерживать ссылку на объект Picker в вашем классе панели инструментов.

Это, вероятно, должно быть более четко документировано вверх по течению.

+0

Большое спасибо за объяснение и совет, я думаю, было бы лучше написать код с двумя строками вместо двух меньших. Еще раз большое спасибо! – Mat

Смежные вопросы