Interação entre Múltiplas Janelas no PyQt5

No desenvolvimento de interfaces gráficas com PyQt5, a comunicação entre múltiplas janelas é uma necessidade comum. Este artigo explora duas abordagens para essa interação: uma sem o uso de sinais e slots, que pode levar a um forte acoplametno, e outra utilziando sinais e slots para promover um design mais modular e flexível.

Método 1: Interação Direta sem Sinais e Slots

Nesta abordagem, uma janela acesssa diretamente os métodos e controles de outra. Embora simples, isso cria dependência rígida, pois alterações em uma janela podem exigir modificações na outra.

Exemplo de código para a janela principal:

"""
Interação entre múltiplas janelas - abordagem direta
"""

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from DateTimeDialog import DateTimeDialog


class MainWindow(QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("Interação Direta entre Janelas")

        self.inputField = QLineEdit(self)
        self.btnFirst = QPushButton("Abrir Primeiro Diálogo")
        self.btnFirst.clicked.connect(self.invokeFirstDialog)

        self.btnSecond = QPushButton("Abrir Segundo Diálogo")
        self.btnSecond.clicked.connect(self.invokeSecondDialog)

        layoutGrid = QGridLayout()
        layoutGrid.addWidget(self.inputField)
        layoutGrid.addWidget(self.btnFirst)
        layoutGrid.addWidget(self.btnSecond)

        self.setLayout(layoutGrid)

    def invokeFirstDialog(self):
        dialog = DateTimeDialog(self)
        if dialog.exec_():
            dateTime = dialog.retrieveDateTime()
            self.inputField.setText(dateTime.date().toString())
        dialog.destroy()

    def invokeSecondDialog(self):
        date, time, confirmed = DateTimeDialog.fetchDateTime()
        self.inputField.setText(date.toString())
        if confirmed:
            print("Ação confirmada")
        else:
            print("Ação cancelada")


if __name__ == "__main__":
    app = QApplication(sys.argv)

    mainWin = MainWindow()
    mainWin.show()

    sys.exit(app.exec_())

Arquivo DateTimeDialog.py (diálogo de seleção de data):

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class DateTimeDialog(QDialog):
    def __init__(self, parent=None):
        super(DateTimeDialog, self).__init__(parent)
        self.setWindowTitle("Selecionar Data e Hora")

        mainLayout = QVBoxLayout(self)
        self.dateTimePicker = QDateTimeEdit(self)
        self.dateTimePicker.setCalendarPopup(True)
        self.dateTimePicker.setDateTime(QDateTime.currentDateTime())

        mainLayout.addWidget(self.dateTimePicker)

        buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
        buttonBox.accepted.connect(self.accept)
        buttonBox.rejected.connect(self.reject)

        mainLayout.addWidget(buttonBox)

    def retrieveDateTime(self):
        return self.dateTimePicker.dateTime()

    @staticmethod
    def fetchDateTime(parent=None):
        dialog = DateTimeDialog(parent)
        dialogResult = dialog.exec_()
        selectedDateTime = dialog.retrieveDateTime()
        return (selectedDateTime.date(), selectedDateTime.time(), dialogResult == QDialog.Accepted)

Método 2: Interação via Sinais e Slots

Esta abordagem utiliza sinais e slots para desacoplar as janelas. A janela principal conecta-se aos sinais emitidos pela janela secundária, sem depender diretamente de seus controles internos. Assim, mudanças na interface da janela secundária afetam apenas seu próprio código.

Exemplo de código para a janela principal:

"""
Interação entre múltiplas janelas - usando sinais e slots
"""

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from DateTimePickerDialog import DateTimePickerDialog


class MainInteractionWindow(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.resize(400, 120)
        self.setWindowTitle("Interação com Sinais e Slots")

        self.openBtn = QPushButton("Selecionar Data/Hora")
        self.fieldBuiltIn = QLineEdit(self)
        self.fieldCustom = QLineEdit(self)

        self.fieldBuiltIn.setText("Data do sinal interno")
        self.fieldCustom.setText("Data do sinal personalizado")

        grid = QGridLayout()
        grid.addWidget(self.fieldBuiltIn)
        grid.addWidget(self.fieldCustom)
        grid.addWidget(self.openBtn)

        self.setLayout(grid)

        self.openBtn.clicked.connect(self.showDialog)

    def showDialog(self):
        pickerDialog = DateTimePickerDialog(self)
        # Conectar sinal interno do QDateTimeEdit ao slot da janela principal
        pickerDialog.builtInDateTimeWidget.dateTimeChanged.connect(self.updateFromBuiltInSignal)

        # Conectar sinal personalizado da janela secundária
        pickerDialog.customDateEmitted.connect(self.updateFromCustomSignal)
        pickerDialog.show()

    def updateFromBuiltInSignal(self, dateTime):
        self.fieldBuiltIn.setText(dateTime.toString())

    def updateFromCustomSignal(self, dateString):
        self.fieldCustom.setText(dateString)


if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainInteractionWindow()
    window.show()

    sys.exit(app.exec_())

Arquivo DateTimePickerDialog.py (diálogo com sinais personalizados):

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class DateTimePickerDialog(QDialog):
    customDateEmitted = pyqtSignal(str)  # Sinal personalizado para enviar a data como string

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowTitle("Diálogo de Seleção")

        dialogLayout = QVBoxLayout(self)

        infoLbl = QLabel("O primeiro campo emite um sinal interno\nO segundo emite um sinal personalizado")
        dialogLayout.addWidget(infoLbl)

        self.builtInDateTimeWidget = QDateTimeEdit(self)
        self.builtInDateTimeWidget.setCalendarPopup(True)
        self.builtInDateTimeWidget.setDateTime(QDateTime.currentDateTime())

        self.customDateTimeWidget = QDateTimeEdit(self)
        self.customDateTimeWidget.setCalendarPopup(True)
        self.customDateTimeWidget.setDateTime(QDateTime.currentDateTime())

        dialogLayout.addWidget(self.builtInDateTimeWidget)
        dialogLayout.addWidget(self.customDateTimeWidget)

        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        dialogLayout.addWidget(buttons)

        # Conectar mudança de data/hora no segundo widget à emissão do sinal personalizado
        self.customDateTimeWidget.dateTimeChanged.connect(self.onCustomDateTimeChanged)

    def onCustomDateTimeChanged(self):
        dateTimeStr = self.customDateTimeWidget.dateTime().toString()
        self.customDateEmitted.emit(dateTimeStr)

Tags: pyqt5 sinais_e_slots QDialog janelas_múltiplas QDateTimeEdit

Publicado em 7-12 22:05