zoukankan      html  css  js  c++  java
  • PyQt5 signal and slot

    PyQt5 的 signal 与 slot 有所改变,例如,先定义一个 ZeroSignal 类:

    class ZeroSignal(QObject):
        atzero = pyqtSignal(int)

    使用时,一是绑定 slot 如下:

        self.zerosig = ZeroSignal()
           self.zerosig.atzero[int].connect(self.countZero)

    然后是找个机会发动之:

    def checkZero(self):
            if self.value() == 0:
                self.zerosig.atzero.emit(self.value())

    大约如此,完整代码如下:

    import os
    import sys
    
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    from PyQt5.QtWidgets import *
    
    class ZeroSignal(QObject):
        atzero = pyqtSignal(int)
    
    class ZeroSpinBox(QSpinBox):
        def __init__(self, parent=None):
            super(ZeroSpinBox, self).__init__(parent)
            self.zeros = 0
            self.valueChanged[int].connect(self.checkZero)
            self.zerosig = ZeroSignal()
            self.zerosig.atzero[int].connect(self.countZero)
    
        def countZero(self, v):
            if v == 0:
                self.zeros += 1
                print(self.zeros)
    
        def checkZero(self):
            if self.value() == 0:
                self.zerosig.atzero.emit(self.value())
                
    
    
    
    class Form(QDialog):
        def __init__(self, parent=None):
            super(Form, self).__init__(parent)
            
            dial = QDial()
            dial.setNotchesVisible(True)
            spin = ZeroSpinBox()
            layout = QHBoxLayout()
            layout.addWidget(dial)
            layout.addWidget(spin)
            self.setLayout(layout)
            dial.valueChanged.connect(spin.setValue)
            spin.valueChanged.connect(dial.setValue)
            spin.valueChanged.connect(self.emitZero)
            self.setWindowTitle("Sinal and Solt")
            
            self.zerobox = spin 
            self.zerobox.zerosig.atzero[int].connect(self.annouce)
    
        def emitZero(self, v):
            if v == 0:
                self.zerobox.zerosig.atzero.emit(self.zerobox.zeros)
    
        def annouce(self, v):
            print("zero count: %d" % v)  # print two times because add ZeroSpinBox emit once
    
    def run():
        app = QApplication(sys.argv)
        form = Form()
        form.show()
        app.exec_()
    
    if __name__ == '__main__':
        run()

    关于对话框的代码如下,尤其 NumberLiveDialog 值得推荐:

    import sys,math,random,string
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    from PyQt5.QtWidgets import *
    
    class NumberDialog(QDialog):
        def __init__(self, format, parent=None):
            super(NumberDialog, self).__init__(parent)
    
            thousandsLabel = QLabel("&Thousands separator:")
            self.thousandsEdit = QLineEdit(format["thousandsseparator"])
            thousandsLabel.setBuddy(self.thousandsEdit)
            decimalMarkerLabel = QLabel("Decimal $marker:")
            self.decimalMarkerEdit = QLineEdit(format["decimalmarker"])
            decimalMarkerLabel.setBuddy(self.decimalMarkerEdit)
            decimalPlacesLabel = QLabel("&Decimal places:")
            self.decimalPlacesSpinBox = QSpinBox()
            decimalPlacesLabel.setBuddy(self.decimalPlacesSpinBox)
            self.decimalPlacesSpinBox.setRange(0, 6)
            self.decimalPlacesSpinBox.setValue(format["decimalplaces"])
            self.redNegativesCheckBox = QCheckBox("&Red negative numbers")
            self.redNegativesCheckBox.setChecked(format["rednegatives"])
    
            buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
            self.format = format.copy()
    
            grid = QGridLayout()
            grid.addWidget(thousandsLabel, 0, 0)
            grid.addWidget(self.thousandsEdit, 0, 1)
            grid.addWidget(decimalMarkerLabel, 1, 0)
            grid.addWidget(self.decimalMarkerEdit, 1, 1)
            grid.addWidget(decimalPlacesLabel, 2, 0)
            grid.addWidget(self.decimalPlacesSpinBox, 2, 1)
            grid.addWidget(self.redNegativesCheckBox, 3, 0, 1, 2)
            grid.addWidget(buttonBox, 4, 0, 1, 2)
            self.setLayout(grid)
    
            buttonBox.accepted.connect(self.accept)
            buttonBox.rejected.connect(self.reject)
            self.setWindowTitle("Number format")
    
        def accept(self):
            class ThousandsError(Exception): pass 
            class DecimalError(Exception): pass 
            Punctuation = frozenset(" ,;:.")
    
            thousands = str(self.thousandsEdit.text())
            decimal = str(self.decimalMarkerEdit.text())
            try:
                if len(decimal) == 0:
                    raise DecimalError("The decimal marker may not be empty.")
                if len(thousands) > 1:
                    raise ThousandsError("The thousands separator may only be empty or one charactor.")
                if len(decimal) > 1:
                    raise DecimalError("The decimal marker must be one character.")
                if thousands == decimal:
                    raise ThousandsError("The thousands separator and decimal marker must be different.")
                if thousands and thousands not in Punctuation:
                    raise ThousandsError("The thousands separator must be in Punctuation.")
                if decimal and decimal not in Punctuation:
                    raise DecimalError("The decimal marker must be a punctuation symbol.")
            except ThousandsError as e:
                QMessageBox.warning(self, "Thousands separator error:", str(e))
                self.thousandsEdit.selectAll()
                self.thousandsEdit.setFocus()
                return 
            except DecimalError as e:
                QMessageBox.warning(self, "Decimal marker error:", str(e))
                self.decimalMarkerEdit.selectAll()
                self.decimalMarkerEdit.setFocus()
                return
    
            self.format["thousandsseparator"] = thousands
            self.format["decimalmarker"] = decimal
            self.format["decimalplaces"] = self.decimalPlacesSpinBox.value()
            self.format["rednegatives"] = self.redNegativesCheckBox.isChecked()
    
            QDialog.accept(self)
    
        def numberFormat(self):
            return self.format
    
    class NumberModelessDialog(QDialog):
        changed = pyqtSignal()
    
        def __init__(self, format, parent=None):
            super(NumberModelessDialog, self).__init__(parent)
            self.setAttribute(Qt.WA_DeleteOnClose)
            punctuationRe = QRegularExpression(r"[ ,;:.]")
    
            thousandsLabel = QLabel("&Thousands separator:")
            self.thousandsEdit = QLineEdit(format["thousandsseparator"])
            thousandsLabel.setBuddy(self.thousandsEdit)
            self.thousandsEdit.setMaxLength(1)
            self.thousandsEdit.setValidator(QRegularExpressionValidator(punctuationRe, self))
    
            decimalMarkerLabel = QLabel("Decimal $marker:")
            self.decimalMarkerEdit = QLineEdit(format["decimalmarker"])
            decimalMarkerLabel.setBuddy(self.decimalMarkerEdit)
            self.decimalMarkerEdit.setMaxLength(1)
            self.decimalMarkerEdit.setValidator(QRegularExpressionValidator(punctuationRe, self))
    
            decimalPlacesLabel = QLabel("&Decimal places:")
            self.decimalPlacesSpinBox = QSpinBox()
            decimalPlacesLabel.setBuddy(self.decimalPlacesSpinBox)
            self.decimalPlacesSpinBox.setRange(0, 6)
            self.decimalPlacesSpinBox.setValue(format["decimalplaces"])
            
            self.redNegativesCheckBox = QCheckBox("&Red negative numbers")
            self.redNegativesCheckBox.setChecked(format["rednegatives"])
    
            buttonBox = QDialogButtonBox(QDialogButtonBox.Apply | QDialogButtonBox.Close)
            self.format = format
    
            grid = QGridLayout()
            grid.addWidget(thousandsLabel, 0, 0)
            grid.addWidget(self.thousandsEdit, 0, 1)
            grid.addWidget(decimalMarkerLabel, 1, 0)
            grid.addWidget(self.decimalMarkerEdit, 1, 1)
            grid.addWidget(decimalPlacesLabel, 2, 0)
            grid.addWidget(self.decimalPlacesSpinBox, 2, 1)
            grid.addWidget(self.redNegativesCheckBox, 3, 0, 1, 2)
            grid.addWidget(buttonBox, 4, 0, 1, 2)
            self.setLayout(grid)
            
            buttonBox.button(QDialogButtonBox.Apply).clicked.connect(self.apply)
            buttonBox.rejected.connect(self.reject)
            self.setWindowTitle("Number format")
    
        def apply(self):
            thousands = str(self.thousandsEdit.text())
            decimal = str(self.decimalMarkerEdit.text())
            if thousands == decimal:
                QMessageBox.warning(self, "Thousands separator error")
                self.thousandsEdit.selectAll()
                self.thousandsEdit.setFocus()
                return 
            if len(decimal) == 0:
                QMessageBox.warning(self, "Decimal marker cannot is empty.")
                self.decimalMarkerEdit.selectAll()
                self.decimalMarkerEdit.setFocus()
                return
    
            self.format["thousandsseparator"] = thousands
            self.format["decimalmarker"] = decimal
            self.format["decimalplaces"] = self.decimalPlacesSpinBox.value()
            self.format["rednegatives"] = self.redNegativesCheckBox.isChecked()
    
            self.changed.emit()
    
    class NumberLiveDialog(QDialog):
        changed = pyqtSignal()
    
        def __init__(self, format, callback, parent=None):
            super(NumberLiveDialog, self).__init__(parent)
            self.format = format
            self.callback = callback
            
            self.setAttribute(Qt.WA_DeleteOnClose)
            punctuationRe = QRegularExpression(r"[ ,;:.]")
    
            thousandsLabel = QLabel("&Thousands separator:")
            self.thousandsEdit = QLineEdit(format["thousandsseparator"])
            thousandsLabel.setBuddy(self.thousandsEdit)
            self.thousandsEdit.setMaxLength(1)
            self.thousandsEdit.setValidator(QRegularExpressionValidator(punctuationRe, self))
    
            decimalMarkerLabel = QLabel("Decimal $marker:")
            self.decimalMarkerEdit = QLineEdit(format["decimalmarker"])
            decimalMarkerLabel.setBuddy(self.decimalMarkerEdit)
            self.decimalMarkerEdit.setMaxLength(1)
            self.decimalMarkerEdit.setValidator(QRegularExpressionValidator(punctuationRe, self))
    
            decimalPlacesLabel = QLabel("&Decimal places:")
            self.decimalPlacesSpinBox = QSpinBox()
            decimalPlacesLabel.setBuddy(self.decimalPlacesSpinBox)
            self.decimalPlacesSpinBox.setRange(0, 6)
            self.decimalPlacesSpinBox.setValue(format["decimalplaces"])
            
            self.redNegativesCheckBox = QCheckBox("&Red negative numbers")
            self.redNegativesCheckBox.setChecked(format["rednegatives"])
    
            grid = QGridLayout()
            grid.addWidget(thousandsLabel, 0, 0)
            grid.addWidget(self.thousandsEdit, 0, 1)
            grid.addWidget(decimalMarkerLabel, 1, 0)
            grid.addWidget(self.decimalMarkerEdit, 1, 1)
            grid.addWidget(decimalPlacesLabel, 2, 0)
            grid.addWidget(self.decimalPlacesSpinBox, 2, 1)
            grid.addWidget(self.redNegativesCheckBox, 3, 0, 1, 2)
            self.setLayout(grid)
            
            self.thousandsEdit.textEdited.connect(self.checkAndFix)
            self.decimalMarkerEdit.textEdited.connect(self.checkAndFix)
            self.decimalPlacesSpinBox.valueChanged.connect(self.apply)
            self.redNegativesCheckBox.toggled.connect(self.apply)
    
            self.setWindowTitle("Number format")
    
        def checkAndFix(self):
            thousands = self.thousandsEdit.text()
            decimal = self.decimalMarkerEdit.text()
            if thousands == decimal:
                self.thousandsEdit.clear()
                self.thousandsEdit.setFocus()
            if len(decimal) == 0:
                self.decimalMarkerEdit.setText(".")
                self.decimalMarkerEdit.selectAll()
                self.decimalMarkerEdit.setFocus()
            self.apply()
    
        def apply(self):
            thousands = self.thousandsEdit.text()
            decimal = self.decimalMarkerEdit.text()
    
            self.format["thousandsseparator"] = thousands
            self.format["decimalmarker"] = decimal
            self.format["decimalplaces"] = self.decimalPlacesSpinBox.value()
            self.format["rednegatives"] = self.redNegativesCheckBox.isChecked()
    
            self.callback()
    
    class Form(QDialog):
        X_MAX = 26
        Y_MAX = 60
    
        def __init__(self, parent=None):
            super(Form, self).__init__(parent)
    
            self.numberDlg = None
            self.format = { "thousandsseparator":",", "decimalmarker":".", 
                "decimalplaces":2, "rednegatives": False } 
            self.numbers = {}
            for x in range(self.X_MAX):
                for y in range(self.Y_MAX):
                    self.numbers[(x,y)] = 10000 * random.random() - 5000
    
            self.table = QTableWidget()
            modalButton = QPushButton("Set numbe format...(&Modal)")
            modelessButton = QPushButton("Set number format...(M&odeless)")
            liveButton = QPushButton("Set number format...(&Live)")
    
            buttonLayout = QHBoxLayout()
            buttonLayout.addStretch()
            buttonLayout.addWidget(modalButton)
            buttonLayout.addWidget(modelessButton)
            buttonLayout.addWidget(liveButton)
            layout = QVBoxLayout()
            layout.addWidget(self.table)
            layout.addLayout(buttonLayout)
            self.setLayout(layout)
    
            modalButton.clicked.connect(self.setNumberFormatByModal)
            modelessButton.clicked.connect(self.setNumberFormatByModeless)
            liveButton.clicked.connect(self.setNumberFormatByLive)
    
            self.setWindowTitle("Set Number Format")
            self.refreshTable()
    
        def refreshTable(self):
            self.table.clear()
            self.table.setRowCount(self.Y_MAX)
            self.table.setColumnCount(self.X_MAX)
            self.table.setHorizontalHeaderLabels(list(string.ascii_uppercase))
    
            for x in range(self.X_MAX):
                for y in range(self.Y_MAX):
                    fraction, whole = math.modf(self.numbers[(x,y)])
                    sign = "-" if whole < 0 else ""
                    whole = "{0}".format(int(math.floor(abs(whole))))
                    digits = []
                    for i, digit in enumerate(reversed(whole)):
                        if i and i % 3 == 0:
                            digits.insert(0, self.format["thousandsseparator"])
                        digits.insert(0, digit)
                    if self.format["decimalplaces"]:
                        fraction = "{0:.7f}".format(abs(fraction))
                        fraction = self.format["decimalmarker"] + fraction[2:self.format["decimalplaces"]+2]
                    else:
                        fraction = ""
                    text = "{0}{1}{2}".format(sign, "".join(digits), fraction)
                    item = QTableWidgetItem(text)
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    if sign and self.format["rednegatives"]:
                        item.setBackground(Qt.red)
                    self.table.setItem(y, x, item)
    
        def setNumberFormatByModal(self):
            dlg = NumberDialog(self.format, self)
            if dlg.exec_():
                self.format = dlg.numberFormat()
                self.refreshTable()
    
        def setNumberFormatByModeless(self):
            dlg = NumberModelessDialog(self.format, self)
            dlg.show()
            dlg.changed.connect(self.refreshTable)
    
        def setNumberFormatByLive(self):
            if self.numberDlg is None:
                self.numberDlg = NumberLiveDialog(self.format, self.refreshTable, self)
            self.numberDlg.show()
            self.numberDlg.raise_()
            self.numberDlg.activateWindow()
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        form = Form()
        form.show()
        app.exec_()
    numberdlg.py

     关于主窗口 (fileload function need add a bool parameter for recentfiles) 的代码:imagechanger

     运行效果图如下:

            

     《Python GUI Qt 快速开放指南》部分例子: PyQt5-Code

  • 相关阅读:
    Atitit  atiMail atiDns新特性 v2  q39
    Atitit  atiMail atiDns新特性 v2  q39
    Atitit.aticmd v4  新特性q39 添加定时器释放功能
    Atitit.aticmd v4  新特性q39 添加定时器释放功能
    Atitit. Atiposter 发帖机 新特性 poster new feature   v7 q39
    Atitit. Atiposter 发帖机 新特性 poster new feature   v7 q39
    Atitit.编程语言and 自然语言的比较and 编程语言未来的发展
    Atitit.编程语言and 自然语言的比较and 编程语言未来的发展
    atitit.解决struts2 SpringObjectFactory.getClassInstance NullPointerException  v2 q31
    知也atitit.解决struts2 SpringObjectFactory.getClassInstance NullPointerException  v2 q31无涯 - I
  • 原文地址:https://www.cnblogs.com/china_x01/p/7932693.html
Copyright © 2011-2022 走看看