es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Se trata de la interacción entre la señal editingFinished del QLineEdit y la pulsación de un botón.

Tengo un QLineEdit en un QDialog con un QButtonBox en el diálogo. La señal editingFinished de QLineEdit está conectada a una función. La función se llama si QLineEdit pierde el enfoque, como se espera. Cuando se pierde el foco al presionar el botón Ok es donde me confundo. Mi manejador de señal se llama, como se espera, pero la señal accepted() del botón Ok no se emite. ¿Por qué se pierde esta señal? En particular, si el cuadro de diálogo se ejecuta con exec(), al presionar el botón OK cuando el foco está en mi QLineEdit, no se termina el cuadro de diálogo, simplemente se invoca el controlador editingFinished. Mi controlador llama a un QMessageBox y esto es lo que parece estar rompiendo la señal accepted. Si lo comento, entonces obtengo el comportamiento esperado.

¿Está la señal accepted() de QMessageBox interfiriendo con la señal de QButtonBox? Si es así, ¿cuál es la forma correcta de manejar esto?

edición – Ejecute el código a continuación. Haga clic en el cuadro QLineEdit, luego haga clic en Ok. Verá una ventana de mensaje. Haga clic en OK en la ventana de mensaje y volverá al cuadro de diálogo, con el botón Ok con enfoque e indicando el estado “abajo”. Verificando el botón con isDown() devuelve falso, por lo que aparentemente no hace la transición al estado de abajo hasta que myHandler devuelve. Ahora comenta la línea msg_box.exec() y repita los pasos anteriores. Al hacer clic en el botón Ok, se cierra el cuadro de diálogo.

El comportamiento que busco es que la acción del botón Ok (accepted) se ejecute después del retorno de myHandler(). Sé que puedo emitir la señalaccepted o rejected desde myHandler, pero eso requiere comprobar qué botón se presionó. Creo que puedo saber eso comprobando el enfoque, pero espero que haya un enfoque más limpio. Y más generalmente, me pregunto qué está sucediendo con las señales que el botón presionado está emitiendo, pero que parecen estar perdidas o ser capturadas.

from PyQt5 import QtCore, QtGui, QtWidgets
import sys

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(640, 480)
        self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
        self.buttonBox.setGeometry(QtCore.QRect(10, 440, 621, 32))
        self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
        self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
        self.buttonBox.setObjectName("buttonBox")
        self.lineEdit = QtWidgets.QLineEdit(Dialog)
        self.lineEdit.setGeometry(QtCore.QRect(180, 180, 251, 31))
        self.lineEdit.setObjectName("lineEdit")

    self.retranslateUi(Dialog)
    self.buttonBox.accepted.connect(Dialog.accept)
    self.buttonBox.rejected.connect(Dialog.reject)
    QtCore.QMetaObject.connectSlotsByName(Dialog)

def retranslateUi(self, Dialog):
    _translate = QtCore.QCoreApplication.translate
    Dialog.setWindowTitle(_translate("Dialog", "Dialog"))

class MyDialog(QtWidgets.QDialog, Ui_Dialog):
  def __init__(self, parent= None):
    QtWidgets.QDialog.__init__(self)
    self.setupUi(self)
    self.lineEdit.editingFinished.connect(self.myHandler)

def myHandler(self):
    print("My handler called")
    msg_box = QtWidgets.QMessageBox()
    msg_box.setText("Your Message here")
    msg_box.exec()

if __name__ == '__main__':
  app = QtWidgets.QApplication(sys.argv)
  myapp = MyDialog()
  myapp.show()
  sys.exit(app.exec())
Tags:  , , ,

Answer

  1. Avatar for davy.ai

    El problema aquí es que el QMessageBox invocado en myHandler está bloqueando el ciclo de eventos y no permite que la señal accepted() del botón Ok se emita hasta que se cierre el cuadro de diálogo. Esto lleva a un comportamiento inesperado cuando se ejecuta el QDialog.

    Una forma de manejar esto es usar la clase QTimer para retrasar la ejecución del cuadro de diálogo hasta que se haya emitido la señal accepted(). Esto se puede hacer modificando la función myHandler de la siguiente manera:

      def myHandler(self):
        print("Mi manejador llamado")
        QtWidgets.QApplication.processEvents() # Procesa los eventos pendientes para permitir que se emita la señal accepted()
        QtCore.QTimer.singleShot(0, lambda: self.showMessage("Su mensaje aquí")) # Retrasa el cuadro de diálogo hasta después de que se emita la señal
    
      def showMessage(self, message):
        msg_box = QtWidgets.QMessageBox()
        msg_box.setText(message)
        msg_box.exec()
    

    Esta versión modificada de myHandler primero llama a QtWidgets.QApplication.processEvents() para procesar cualquier evento pendiente y permitir que se emita la señal accepted(). Luego, usa QtCore.QTimer.singleShot() para retrasar la invocación de la función showMessage hasta después de que se haya emitido la señal. showMessage luego crea y muestra el cuadro de diálogo como antes.

    Este enfoque evita la necesidad de probar qué botón se presionó y permite una ejecución más limpia del código.

    En general, es importante ser consciente de la obstrucción del ciclo de eventos en las aplicaciones de PyQt5 para evitar comportamientos inesperados. La clase QTimer puede ser una herramienta útil para retrasar la ejecución y evitar la obstrucción.

Comments are closed.