PyQt4编程之模态与非模态对话框(一)

模态对话框(Modal Dialogue Box,又叫做模式对话框),是指在用户想要对对话框以外的应用程序进行操作时,必须首先对该对话框进行响应。如单击【确定】或【取消】按钮等将该对话框关闭。------------以上内容摘自360百科

对话框按照模态可以分为模态对话框和非模态对话框,按照智能程度又可以分为简易,标准和智能。大多数模态情况下,对话框包括简易和标准型的。


按照我的理解,简易型和标准型的其实基础架构差不多,只是标准型的“格式更标准”。所以先直接上一个标准型的例子来分析一下。

我们需要做好的对话框效果如下图所示。

就是在主界面点击某一个按钮,然后弹出该对话框对显示数据的格式进行设置。先上代码。


import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Widget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        button  =   QPushButton(QIcon("web.png"),"click me",self)
        button.move(100,100)
        button.clicked.connect(self.setNumberFormat1)
        self.format =   dict(thousandsseparator=',',decimalmarker='.',decimalplaces=2,rednegatives=True)
        self.resize(200,300)

    def setNumberFormat1(self):		#the first Point
        dialog  =   NumberFormatDlg(self.format,self)
        if dialog.exec_():
            self.format =   dialog.numberFormat()
            self.refreshTable()

class NumberFormatDlg(QDialog):		#the second point
    def __init__(self,format,parent=None):
        super(NumberFormatDlg,self).__init__(parent)    

        thousandsLabel  =   QLabel("&Thousands seperator")    
        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()	# notice

        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)

        self.connect(buttonBox.button(QDialogButtonBox.Ok),SIGNAL("clicked()"),self,SLOT("accept()"))                                    self.connect(buttonBox,SIGNAL("rejected()"),self,SLOT("reject()"))  
        self.setWindowTitle("Set Number Format (Modal)")

    def numberFormat(self):
        return self.format

    def accept(self):		#override 'accept()' method
        class ThousandsError(Exception):        #inherit the class Exception
            def __init__(self,message):
                Exception.__init__(self)
                self.message=message
        class DecimalError(Exception):
            def __init__(self,message):
                Exception.__init__(self)
                self.message=message
        Punctuation =   frozenset(" ,;.")

        thousands   =   unicode(self.thousandsEdit.text())                                                                               decimal =   unicode(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 character.")

            if len(decimal) > 1:
                raise DecimalError("The decimal marker must be one character")

            if thousands == decimal:
                raise ThousandsError("The thousands separator and the decimal marker must be different.")

            if thousands and thousands  not in Punctuation:     #"and not in"
                raise ThousandsError("The thousands separator must be a punctuation sumbol.")

        except ThousandsError, e:
            QMessageBox.warning(self,"Thousands Separator", unicode(e.message))       #QMessageBox's warning can create a new 'warning widget'
            self.thousandsEdit.selectAll()
            self.thousandsEdit.setFocus()
            return
        except DecimalError, e:
            QMessageBox.warning(self,"D",unicode(e.message))
            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()   #the CheckBox has 'isChecked()' which can get the vaule of the CheckBox
        QDialog.accept(self)

app =   QApplication(sys.argv)
widget  =   Widget()
widget.show()

app.exec_()

好,然后咱就按照套路分析一下。

框架讲解
框架就好比一篇文章的行文思路,是写这种代码所必须的。
第一部分:setNumberFormat1这个函数就是来弹出格式设置对话框,并对主窗口里面的数据格式更新。可以把它看成主窗口和对话框之间的桥梁。

第二部分:第二部分就是对话框这个类的实现了。
                  首先是init函数,设置控件,排出布局,连接按钮的信号和槽,前面的文章已经分析过类似的,所以也不需要多讲。

                  然后就是accept函数了,我们这里对它进行了重载。为什么要进行重载呢??进行重载一般是因为原来的函数不能实现我们想要的功能。
                  我们这里想要的功能是当我们点击“Ok”这个button的时候,对‘对话框’的各项的值进行验证,如果符合,则返回True给exec_(),否则回到对话框。而原来的accept函数只是简单地给exec_()返回一个True,除此之外什么都不干。(验证按验证的对象及其之间的关系分为窗口部件级验证和窗体级验证,按照时间先后分为预防式验证和提交后验证,这里明显是窗口部件级验证和提交后验证)


实现分析

1.代码实现部分需要注意的是"self.format =   format.copy()",在智能对话框里面,这一句是self.format = format.
用copy的原因是我们想传格式字典的副本,这样当改变对话框内的格式字典的时候不会影响原来的初始字典。

2.在accept的开头,我们创建了两个异常类,然后用try和except捕获异常。


下一篇结合非模态实例来讲解模态与非模态的区别。



你可能感兴趣的:(#,PyQt(停更))