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)