Reputation: 347
I am working with a custom QMenu which executes some methods. The menu has three options: a delete row option, a toggle variable option and a debug option, which prints the value of the toggleing variable. The code is not properly executed. Sometimes the debug button doesnt work and it suddely gets executed many times. The toggle option needs to be clicked twice to work, i dont know why. This is my MRE:
# -*- coding: utf-8 -*-
from PyQt5.QtCore import Qt, QRect, pyqtSlot
from PyQt5.QtGui import QCursor
from PyQt5.QtWidgets import QWidget, QPushButton, QHBoxLayout, QMainWindow, QLabel, QMenu, \
QApplication, QVBoxLayout, QListWidgetItem, QListWidget, QAction
class Punto(QWidget):
def __init__(self, parent, internal_id, name):
QWidget.__init__(self)
# Toggle variable
self.render = True
self.customContextMenuRequested.connect(self.context_menu)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.menu = QMenu()
self.borrar = QAction("Delete")
self.ver = QAction("Toggle")
self.debug = QAction("Debug")
self.ver.setCheckable(True)
self.ver.setChecked(True)
self.parent = parent
self.id = internal_id
label = QLabel(name)
hbox = QHBoxLayout()
hbox.addWidget(label)
hbox.addStretch(1)
self.setLayout(hbox)
def context_menu(self):
self.menu.addAction(self.borrar)
self.borrar.triggered.connect(self.delete)
self.menu.addAction(self.ver)
self.ver.triggered.connect(self.change)
self.menu.addAction(self.debug)
self.debug.triggered.connect(self.debugg)
self.menu.exec(QCursor.pos())
@pyqtSlot()
def debugg(self):
print(f"Render: {self.render}")
@pyqtSlot()
def change(self):
if self.ver.isChecked():
self.ver.setChecked(False)
self.render = False
else:
self.ver.setChecked(True)
self.render = True
@property
def itemid(self):
return self.id
@pyqtSlot()
def delete(self):
self.parent.delete_point(self.id)
class Ventana(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setFixedSize(200, 200)
widget_central = QWidget(self)
boton_punto = QPushButton(widget_central)
boton_punto.setGeometry(QRect(0, 0, 200, 20))
boton_punto.clicked.connect(self.crear_punto)
boton_punto.setText("Create")
widget_punto = QWidget(widget_central)
widget_punto.setGeometry(QRect(0, 20, 200, 200))
vertical_punto = QVBoxLayout(widget_punto)
vertical_punto.setContentsMargins(0, 0, 0, 0)
self.lista_puntos = QListWidget(widget_punto)
vertical_punto.addWidget(self.lista_puntos)
self.id_punto = 0
self.setCentralWidget(widget_central)
def crear_punto(self):
# Add placeholder item to List
item = QListWidgetItem()
self.lista_puntos.addItem(item)
# Create Custom Widget
punto = Punto(self, self.id_punto, "A")
self.id_punto += 1
item.setSizeHint(punto.minimumSizeHint())
# Set the punto widget to be displayed within the placeholder item
self.lista_puntos.setItemWidget(item, punto)
def delete_point(self, idd):
for indx in range(self.lista_puntos.count()):
item = self.lista_puntos.item(indx)
widget = self.lista_puntos.itemWidget(item)
if widget.id == idd:
self.lista_puntos.takeItem(self.lista_puntos.row(item))
break
if __name__ == "__main__":
MainEvent = QApplication([])
main_app = Ventana()
main_app.show()
MainEvent.exec()
Upvotes: 1
Views: 602
Reputation: 243887
You have 2 errors:
By default a QAction already makes the change of state so it is not necessary that you implement it, but you are doing it, that is, by default the QAction changes from on to off (or vice versa) but you by code change it from off a on (or vice versa) that when done in ms the change is not observed. So instead of connecting the triggered signal, use the toggled signal and just change the render.
When you connect a signal to the same slot "n" times the slot is invoked "n" times, and in your case you are connecting it every time the context_menu method is invoked, there are at least 2 solutions: make the connection only once or use the type of connection Qt::UniqueConnection, in my solution I will use the first one.
Considering the above, the solution is:
class Punto(QWidget):
def __init__(self, parent, internal_id, name):
QWidget.__init__(self)
# Toggle variable
self.render = True
self.customContextMenuRequested.connect(self.context_menu)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.menu = QMenu()
self.borrar = QAction("Delete")
self.ver = QAction("Toggle")
self.debug = QAction("Debug")
self.ver.setCheckable(True)
self.ver.setChecked(True)
self.parent = parent
self.id = internal_id
label = QLabel(name)
hbox = QHBoxLayout(self)
hbox.addWidget(label)
hbox.addStretch(1)
self.borrar.triggered.connect(self.delete)
self.ver.toggled.connect(self.change)
self.debug.triggered.connect(self.debugg)
self.menu.addAction(self.borrar)
self.menu.addAction(self.ver)
self.menu.addAction(self.debug)
def context_menu(self):
self.menu.exec(QCursor.pos())
@pyqtSlot()
def debugg(self):
print(f"Render: {self.render}")
@pyqtSlot(bool)
def change(self, state):
self.render = self.ver.isChecked()
@property
def itemid(self):
return self.id
@pyqtSlot()
def delete(self):
self.parent.delete_point(self.id)
Upvotes: 2