Contextmenü erscheint nicht

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
freak2003
User
Beiträge: 8
Registriert: Freitag 26. November 2010, 14:58

Hallo alle zusammen,

ich programmiere zur Zeit mit pyqt ein gui. Ich habe eine QGraphicsScene die QItems enthält. Jetzt soll bei einem Rechtsklick ein Contextmenü erscheinen. Dabei soll es ein Unterschied sein, ob ich auf ein Item klicke, oder in einen leeren Bereich. Jetzt habe ich für das MousePressEvent den Aufruf angelegt. Aber leider passiert gar nix. Kann mir jemand helfen??

Code: Alles auswählen

class graphicsScene(QtGui.QGraphicsScene):
  def __init__ (self, parent = None):
    super(graphicsScene, self).__init__ (parent)

  def contextMenuEvent(self, event):
    self.popMenu = QtGui.QMenu()
    self.popMenu.addAction(QtGui.QAction('test0', None))
    self.popMenu.addAction(QtGui.QAction('test1', None))
    self.popMenu.addSeparator()
    self.popMenu.addAction(QtGui.QAction('test2', None))
    self.popMenu.exec_(event.globalPos())

  def mousePressEvent(self, event):
    super(graphicsScene, self).mousePressEvent(event)
    pos = event.scenePos()
    item = self.itemAt(pos)

    if event.button() == QtCore.Qt.LeftButton:
      #do something

    elif event.button() == QtCore.Qt.RightButton:
      self.contextMenuEvent(event)
EmaNymton
User
Beiträge: 174
Registriert: Sonntag 30. Mai 2010, 14:07

Hast du denn mal getestet, ob der event überhaupt an deine graphicsScene gesendet wird, indem du z.B. mal ein print in die überschriebene Methode einfügst?

Ansonsten kann ich mir nämlich sehr gut vorstellen, dass der Event an die Items geht und deine graphicsScene davon überhaupt nichts mitbekommt.
freak2003
User
Beiträge: 8
Registriert: Freitag 26. November 2010, 14:58

Hallo,

ja hab ich probiert. Er geht schon in die Funktion "contextMenuEvent" rein. Aber dort passiert nix.
lunar

@freak2003 Wie kommst Du auf ".globalPos()"? Laut Dokumentation gibt es diese Methode nicht. Versuche stattdessen ".screenPos()".

Zudem ist es iirc nicht nötig, "mousePressEvent()" zu implementieren, um ein Kontextmenü anzuzeigen. Das Framework sollte ".contextMenuEvent()" automatisch auslösen, wenn Kontextmenü angezeigt werden soll. Insbesondere sorgt das Framework dafür, dass dieses Event entsprechend den Konventionen des Systems ausgelöst wird. Der Klick mit der rechten Maustaste ist nicht auf jedem System der entsprechende Auslöser.
freak2003
User
Beiträge: 8
Registriert: Freitag 26. November 2010, 14:58

Das mit dem MousePressEvent mache ich, weil beim Linksklick Items platziert werden sollen. Mit ".screenPos()" passiert genau so wenig etwas.
lunar

@freak2003 Wenn wir Dir weiterhelfen sollen, dann werden wir jetzt wohl vollständigen und ausführbaren Quelltext brauchen, damit wir den Fehler reproduzieren können.
freak2003
User
Beiträge: 8
Registriert: Freitag 26. November 2010, 14:58

Ich hab mal ein kleines Beispiel mit meinem Code erzeugt. Ich hoffe das hilft weiter!!

Code: Alles auswählen

import math, sys
from PyQt4 import QtCore, QtGui

from gui import Ui_MainWindow


#=========================================================================================
class graphicsScene(QtGui.QGraphicsScene):
  def __init__ (self, parent = None):
    super(graphicsScene, self).__init__ (parent)
    self.tmp_item = []
    self.gui = ((self.parent()).parent()).parent()

  def contextMenuEvent(self, event):
    print("contextMenu")
    self.popMenu = QtGui.QMenu()
    self.popMenu.addAction(QtGui.QAction('test0', None))
    self.popMenu.addAction(QtGui.QAction('test1', None))
    self.popMenu.addSeparator()
    self.popMenu.addAction(QtGui.QAction('test2', None))
    self.popMenu.exec_(event.screenPos())
      
  def mousePressEvent(self, event):
    '''Definiert was beim klicken mit der Maus passieren soll'''
    super(graphicsScene, self).mousePressEvent(event)
    pos = event.scenePos()
    item = self.itemAt(pos)
    
    if event.button() == QtCore.Qt.LeftButton:
      if item is None:
        self.gui.max_node = self.gui.max_node + 1
        item = Node(self, self.gui.max_node)
        position = QtCore.QPointF(event.scenePos())
        item.setPos(position.x() , position.y())
        self.addItem(item)
            
    elif event.button() == QtCore.Qt.RightButton:
      print("right")
      self.contextMenuEvent(event)


#=========================================================================================
class MainWindow(Ui_MainWindow):
  def generateStructures(self):
    '''GraphicScene einrichten'''
    scene = graphicsScene(self.graphicsView)
    scene.setItemIndexMethod(QtGui.QGraphicsScene.NoIndex)
    scene.setSceneRect(-365, -285, 730, 570)
    self.graphicsView.setScene(scene)
    
    self.graphicsView.setViewportUpdateMode(QtGui.QGraphicsView.BoundingRectViewportUpdate)
    self.graphicsView.setTransformationAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
    self.graphicsView.setResizeAnchor(QtGui.QGraphicsView.AnchorViewCenter)
  
  def scaleView(self, scaleFactor):
    '''Ansicht scalieren'''
    factor = self.graphicsView.matrix().scale(scaleFactor, scaleFactor).mapRect(QtCore.QRectF(0, 0, 1, 1)).width()

    if factor < 0.07 or factor > 100:
      return

    self.graphicsView.scale(scaleFactor, scaleFactor)
  
#=========================================================================================
class MyForm(QtGui.QMainWindow):
  def __init__(self, parent = None):
    QtGui.QWidget.__init__(self, parent)
    self.ui = MainWindow()
    self.ui.setupUi(self)
    self.ui.generateStructures()
    
    self.max_node = 1
    
    # Slots für Schaltflächen einrichten  
    self.connect(self.ui.pushButtonBeenden, 
            QtCore.SIGNAL("clicked()"), self.onClose)
        
    # Menüeinträge Funktionen zuweisen
    self.ui.actionNeu.triggered.connect(self.onNeu)
    self.ui.actionOeffnen.triggered.connect(self.onLoad)
    self.ui.actionSpeichern.triggered.connect(self.onSave)
    self.ui.actionBeenden.triggered.connect(self.onClose)

  def onNeu(self):
    self.ui.graphicsView.scene().clear()
    
  def onLoad(self):
    pass
  
  def onSave(self):
    pass

  def onClose(self):
    '''Hauptfenster beenden''' 
    self.close()
    

#=========================================================================================
class Node(QtGui.QGraphicsItem):
  '''
  Grafische Darstellung eines Knoten des Graphen
  '''
  Type = QtGui.QGraphicsItem.UserType + 1

  def __init__(self, graphWidget, nr = None):
    super(Node, self).__init__()
    
    self.value = nr
    
    self.graph = graphWidget
    self.newPos = QtCore.QPointF()

    self.setFlag(QtGui.QGraphicsItem.ItemIsMovable)
    self.setFlag(QtGui.QGraphicsItem.ItemSendsGeometryChanges)
    self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
    self.setZValue(1)

  def type(self):
    '''Gibt den item-typ zurück'''
    return Node.Type

  def advance(self):
    if self.newPos == self.pos():
      return False

    self.setPos(self.newPos)
    return True

  def boundingRect(self):
    adjust = 2.0
    return QtCore.QRectF(-10 - adjust, -10 - adjust, 23 + adjust,
            23 + adjust)

  def paint(self, painter, option, widget):
    '''Überschreibt die paint-methode zu zeichnen eines Knoten'''
    node_size = 20
    painter.setPen(QtCore.Qt.NoPen)
    painter.setBrush(QtCore.Qt.darkGray)
    painter.drawEllipse(int(math.floor(-node_size * 0.35)), int(math.floor(-node_size * 0.35)), 
                        node_size, node_size)
    
    gradient = QtGui.QRadialGradient(-3, -3, node_size / 2)
    if option.state & QtGui.QStyle.State_Sunken:
      gradient.setCenter(3, 3)
      gradient.setFocalPoint(3, 3)
      gradient.setColorAt(1, QtGui.QColor(QtCore.Qt.yellow).light(120))
      gradient.setColorAt(0, QtGui.QColor(QtCore.Qt.darkYellow).light(120))
    else:
      gradient.setColorAt(0, QtCore.Qt.yellow)
      gradient.setColorAt(1, QtCore.Qt.darkYellow)
    painter.setBrush(QtGui.QBrush(gradient))
    
    painter.setPen(QtGui.QPen(QtCore.Qt.black, 0))
    painter.drawEllipse(int(math.floor(-node_size * 0.5)), int(math.floor(-node_size * 0.5)), 
                        node_size, node_size)

    painter.setFont(QtGui.QFont('DejaVu Sans', int(math.floor(node_size * 0.4))))
    if int(self.value) <= 9:
      painter.drawText(int(math.floor(-node_size * 0.2)), int(math.floor(node_size * 0.2)), str(self.value))
    else:
      painter.drawText(int(math.floor(-node_size * 0.3)), int(math.floor(node_size * 0.2)), str(self.value))

  def mousePressEvent(self, event):
    self.update()
    super(Node, self).mousePressEvent(event)

  def mouseReleaseEvent(self, event):
    self.update()
    super(Node, self).mouseReleaseEvent(event)
    

#=========================================================================================
if __name__ == "__main__":
  app = QtGui.QApplication(sys.argv)
  
  myapp = MyForm()
  myapp.show()
  sys.exit(app.exec_())
gui.py

Code: Alles auswählen

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.setEnabled(True)
        MainWindow.resize(977, 663)
        font = QtGui.QFont()
        font.setFamily(_fromUtf8("Arial"))
        MainWindow.setFont(font)
        
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.graphicsView = QtGui.QGraphicsView(self.centralwidget)
        self.graphicsView.setGeometry(QtCore.QRect(20, 20, 741, 591))
        self.graphicsView.setAutoFillBackground(False)
        self.graphicsView.setObjectName(_fromUtf8("graphicsView"))
        
        self.pushButtonBeenden = QtGui.QPushButton(self.centralwidget)
        self.pushButtonBeenden.setGeometry(QtCore.QRect(820, 570, 98, 27))
        font = QtGui.QFont()
        font.setFamily(_fromUtf8("Arial"))
        self.pushButtonBeenden.setFont(font)
        self.pushButtonBeenden.setObjectName(_fromUtf8("pushButtonBeenden"))
        self.groupBox = QtGui.QGroupBox(self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(780, 20, 181, 151))
        font = QtGui.QFont()
        font.setFamily(_fromUtf8("Arial"))

        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtGui.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 977, 21))
        self.menubar.setObjectName(_fromUtf8("menubar"))
        self.menuDatei = QtGui.QMenu(self.menubar)
        self.menuDatei.setObjectName(_fromUtf8("menuDatei"))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)
        self.actionNeu = QtGui.QAction(MainWindow)
        self.actionNeu.setObjectName(_fromUtf8("actionNeu"))
        self.actionOeffnen = QtGui.QAction(MainWindow)
        self.actionOeffnen.setObjectName(_fromUtf8("actionOeffnen"))
        self.actionSpeichern = QtGui.QAction(MainWindow)
        self.actionSpeichern.setObjectName(_fromUtf8("actionSpeichern"))
        self.actionBeenden = QtGui.QAction(MainWindow)
        self.actionBeenden.setObjectName(_fromUtf8("actionBeenden"))

 
        self.menuDatei.addAction(self.actionNeu)
        self.menuDatei.addSeparator()
        self.menuDatei.addAction(self.actionOeffnen)
        self.menuDatei.addAction(self.actionSpeichern)
        self.menuDatei.addSeparator()
        self.menuDatei.addAction(self.actionBeenden)
        self.menubar.addAction(self.menuDatei.menuAction())

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(_translate("MainWindow", "PyGraphEdit", None))
        self.pushButtonBeenden.setToolTip(_translate("MainWindow", "Beendet das Programm", None))
        self.pushButtonBeenden.setText(_translate("MainWindow", "Beenden", None))
        self.menuDatei.setTitle(_translate("MainWindow", "Datei", None))
        self.actionNeu.setText(_translate("MainWindow", "Neu", None))
        self.actionOeffnen.setText(_translate("MainWindow", "Öffnen", None))
        self.actionSpeichern.setText(_translate("MainWindow", "Speichern", None))
        self.actionBeenden.setText(_translate("MainWindow", "Beenden", None))
MainWindow
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Mal so ins Blaue gefragt:

Muss Dein QMenu ggf. mit .show() sichtbar gemacht werden?

Grüße ... bwbg
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
freak2003
User
Beiträge: 8
Registriert: Freitag 26. November 2010, 14:58

@bwbg: Laut Dokumentation soll es mit popup() oder exec_() sichtbar gemacht werden.
freak2003
User
Beiträge: 8
Registriert: Freitag 26. November 2010, 14:58

Ich habs doch noch hinbekommen:

Code: Alles auswählen

def contextMenuEvent(self, event):
    '''Kontextmenu beim Rechtsklick anzeigen'''
    toolbar = QtGui.QToolBar()
    self.actionSettings = toolbar.addAction("Einstellungen", self.gui.onSettings)
  
    self.popMenu = QtGui.QMenu()
    self.popMenu.addAction(self.actionSettings)
      
    self.popMenu.exec_(event.screenPos())
Aber trotzdem vielen Dank an alle die mit überlegt haben.
BlackJack

@freak2003: Innerhalb einer Ereignisbehandlung so viel an das Objekt zu binden erscheint mir komisch.

Edit: Ebenfalls komisch bis falsch ist es in aller Regel wenn man Qt-Typen denen man ein Elternobjekt bei der Erstellung übergeben kann, keines übergibt. Das hat Auswirkungen auf die Speicherverwaltung auf der Qt-Seite. Das könnte vielleicht sogar das Problem sein, das Qt das `QMenu`-Exemplar bereinigt bevor es angezeigt werden soll, weil es zu keinem übergeordneten Qt-Objekt gehört und damit „Freiwild” ist.
Antworten