PyQt5中的组件(widgets)
组件(widgets)是构建一个应用的基础模块。PyQt5有广泛的各式各样的组件,包含按钮,复选按钮,滑块条,和列表框。在这个部分的教程中,我们将学习几种有用的组件:复选按钮(QCheckBox),切换按钮(ToggleButton),滑块条(QSlider),进度条(ProgressBar)和日历组件(QCalendarWidget)。
复选框(QCheckBox)
复选框组件有两种状态:选中和未选中。它是由一个选择框和一个标签组成的。一个应用中,复选框是典型的用来代表有效或无效状态的组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | #!/usr/bin/python3 # -*- coding: utf-8 -*- """ ZetCode PyQt5 tutorial In this example, a QCheckBox widget is used to toggle the title of a window. author: Jan Bodnar website: zetcode.com last edited: January 2015 """ import sys from PyQt5.QtWidgets import QWidget, QCheckBox, QApplication from PyQt5.QtCore import Qt class Example(QWidget): def __init__( self ): super ().__init__() self .initUI() def initUI( self ): cb = QCheckBox( 'Show title' , self ) cb.move( 20 , 20 ) cb.toggle() cb.stateChanged.connect( self .changeTitle) self .setGeometry( 300 , 300 , 250 , 150 ) self .setWindowTitle( 'QCheckBox' ) self .show() def changeTitle( self , state): if state = = Qt.Checked: self .setWindowTitle( 'QCheckBox' ) else : self .setWindowTitle('') if __name__ = = '__main__' : app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_()) |
在我们的例子中,我们创建了一个复选框,用来切换窗口标题。
1 | cb = QCheckBox( 'Show title' , self ) |
这是QCheckBox类的构造方法。
我们需要设置窗口标题,所以我们必须选中复选框。如果不选中复选框,默认情况下复选框不会被选中所以窗口标题也不会被设置。
1 | cb.stateChanged.connect( self .changeTitle) |
将我们自定义的changeTitle()槽方法和stateChanged信号连接。changeTitle()方法将用于切换窗口标题。
1 2 3 4 5 6 | def changeTitle( self , state): if state = = Qt.Checked: self .setWindowTitle( 'QCheckBox' ) else : self .setWindowTitle('') |
复选框组件的状态会传入changeTitle()方法的state参数。如果复选框被选中,我们设置窗口标题。否则,我们把窗口标题设置成一个空字符串。
Figure: QCheckBox
切换按钮
切换按钮是QPushButton的特殊模式。切换按钮有两种状态:按下和没有按下。我们可以通过点击它在两种状态之间切换。下面的列子展示了切换按钮合适出现的情景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | #!/usr/bin/python3 # -*- coding: utf-8 -*- """ ZetCode PyQt5 tutorial In this example, we create three toggle buttons. They will control the background colour of a QFrame. author: Jan Bodnar website: zetcode.com last edited: January 2015 """ import sys from PyQt5.QtWidgets import (QWidget, QPushButton, QFrame, QApplication) from PyQt5.QtGui import QColor class Example(QWidget): def __init__( self ): super ().__init__() self .initUI() def initUI( self ): self .col = QColor( 0 , 0 , 0 ) redb = QPushButton( 'Red' , self ) redb.setCheckable( True ) redb.move( 10 , 10 ) redb.clicked[ bool ].connect( self .setColor) redb = QPushButton( 'Green' , self ) redb.setCheckable( True ) redb.move( 10 , 60 ) redb.clicked[ bool ].connect( self .setColor) blueb = QPushButton( 'Blue' , self ) blueb.setCheckable( True ) blueb.move( 10 , 110 ) blueb.clicked[ bool ].connect( self .setColor) self .square = QFrame( self ) self .square.setGeometry( 150 , 20 , 100 , 100 ) self .square.setStyleSheet( "QWidget { background-color: %s }" % self .col.name()) self .setGeometry( 300 , 300 , 280 , 170 ) self .setWindowTitle( 'Toggle button' ) self .show() def setColor( self , pressed): source = self .sender() if pressed: val = 255 else : val = 0 if source.text() = = "Red" : self .col.setRed(val) elif source.text() = = "Green" : self .col.setGreen(val) else : self .col.setBlue(val) self .square.setStyleSheet( "QFrame { background-color: %s }" % self .col.name()) if __name__ = = '__main__' : app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_()) |
在我们的例子中,我们创建了三个切换按钮和一个QWidget组件。我们把QWidget组件的背景颜色设置为黑色。切换按钮将在红色,绿色和蓝色的RGB值部分进行切换。QWidget组件的背景颜色将取决于哪一个切换按钮被按下。
1 | self .col = QColor( 0 , 0 , 0 ) |
这里是初始化,让RGB值为黑色。
1 2 3 | redb = QPushButton( 'Red' , self ) redb.setCheckable( True ) redb.move( 10 , 10 ) |
要创建切换按钮,就要创建QPushButton,并且调用setCheckable()方法让它可被选中。
1 | redb.clicked[ bool ].connect( self .setColor) |
我们把clicked信号连接到我们定义的方法上。我们使用clicked信号来操作布尔值。
我们获得发生状态切换的按钮。
1 2 | if source.text() = = "Red" : self .col.setRed(val) |
在这种情况下,如果发生切换的是red按钮,我们更新RGB值中的红色部分的颜色值。
1 | self .square.setStyleSheet( "QWidget { background-color: %s }" % self .col.name()) |
我们使用样式表来改变背景颜色。
![Toggle button](http://zetcode.com/img/gui/pyqt5/togglebutton.png)
Figure: Toggle button
滑块条(QSlider)
滑块条(QSlider)组件有一个简单的可调节手柄。这个手柄可以前后拖动。我们可以使用这个方式来选择具体的数值。有时使用滑块条比直接输入数字或使用数值选择框更自然,在我们下面的例子中,我们将显示一个滑块条和一个标签。标签将会显示一个图像。滑块条将控制标签。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #!/usr/bin/python3 # -*- coding: utf-8 -*- """ ZetCode PyQt5 tutorial This example shows a QSlider widget. author: Jan Bodnar website: zetcode.com last edited: January 2015 """ import sys from PyQt5.QtWidgets import (QWidget, QSlider, QLabel, QApplication) from PyQt5.QtCore import Qt from PyQt5.QtGui import QPixmap class Example(QWidget): def __init__( self ): super ().__init__() self .initUI() def initUI( self ): sld = QSlider(Qt.Horizontal, self ) sld.setFocusPolicy(Qt.NoFocus) sld.setGeometry( 30 , 40 , 100 , 30 ) sld.valueChanged[ int ].connect( self .changeValue) self .label = QLabel( self ) self .label.setPixmap(QPixmap( 'mute.png' )) self .label.setGeometry( 160 , 40 , 80 , 30 ) self .setGeometry( 300 , 300 , 280 , 170 ) self .setWindowTitle( 'QSlider' ) self .show() def changeValue( self , value): if value = = 0 : self .label.setPixmap(QPixmap( 'mute.png' )) elif value > 0 and value < = 30 : self .label.setPixmap(QPixmap( 'min.png' )) elif value > 30 and value < 80 : self .label.setPixmap(QPixmap( 'med.png' )) else : self .label.setPixmap(QPixmap( 'max.png' )) if __name__ = = '__main__' : app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_()) |
在我们的例子中,我们模拟了一个音量控制器。通过拖动滑块条的把手,我们可以改变标签的图像。
1 | sld = QSlider(Qt.Horizontal, self ) |
这里我们创建了一个横向的滑块条。
1 2 | self .label = QLabel( self ) self .label.setPixmap(QPixmap( 'mute.png' )) |
我们创建了一个标签组件,并且设置一个初始的无声图片。
1 | sld.valueChanged[ int ].connect( self .changeValue) |
我们把valueChanged
信号连接到我们自定义的 changeValue()方法上。
1 2 3 | if value = = 0 : self .label.setPixmap(QPixmap( 'mute.png' )) ... |
这里实现了根据滑块条的值,我们设置不同的标签图片。在上面的代码中,如果滑块条的值等于零,我们为标签设置mute.png图片。
![QSlider widget](http://zetcode.com/img/gui/pyqt5/qslider.png)
Figure: QSlider widget
进度条(QProgressBar)
当我们处理耗时长的任务时,我们需要用到进度条组件。它通过动画的方式让我们了解任务正在处理中。在PyQt5中,进度条组件提供了横向和纵向的进度条选择。程序员可以设置进度条的最大值和最小值。进度条的默认值是0~99。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #!/usr/bin/python3 # -*- coding: utf-8 -*- """ ZetCode PyQt5 tutorial This example shows a QProgressBar widget. author: Jan Bodnar website: zetcode.com last edited: January 2015 """ import sys from PyQt5.QtWidgets import (QWidget, QProgressBar, QPushButton, QApplication) from PyQt5.QtCore import QBasicTimer class Example(QWidget): def __init__( self ): super ().__init__() self .initUI() def initUI( self ): self .pbar = QProgressBar( self ) self .pbar.setGeometry( 30 , 40 , 200 , 25 ) self .btn = QPushButton( 'Start' , self ) self .btn.move( 40 , 80 ) self .btn.clicked.connect( self .doAction) self .timer = QBasicTimer() self .step = 0 self .setGeometry( 300 , 300 , 280 , 170 ) self .setWindowTitle( 'QProgressBar' ) self .show() def timerEvent( self , e): if self .step > = 100 : self .timer.stop() self .btn.setText( 'Finished' ) return self .step = self .step + 1 self .pbar.setValue( self .step) def doAction( self ): if self .timer.isActive(): self .timer.stop() self .btn.setText( 'Start' ) else : self .timer.start( 100 , self ) self .btn.setText( 'Stop' ) if __name__ = = '__main__' : app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_()) |
在我们的例子中有一个横向进度条和一个按钮。按钮控制滑块条的开始和停止。
1 | self .pbar = QProgressBar( self ) |
这是滑块条类的构造方法。
1 | self .timer = QtCore.QBasicTimer() |
我们用定时器对象来激活进度条。
1 | self .timer.start( 100 , self ) |
为了开启定时器事件,我们调用了start()方法。这个方法有两个参数:定时时间和接收定时器事件的对象。
1 2 3 4 5 6 7 8 9 10 | def timerEvent( self , e): if self .step > = 100 : self .timer.stop() self .btn.setText( 'Finished' ) return self .step = self .step + 1 self .pbar.setValue( self .step) |
每个QObject类和它的子类都有timerEvent()事件处理函数用于处理定时事件。为了对定时器事件作出反馈,我们重新实现了这个事件处理函数。
1 2 3 4 5 6 7 8 9 | def doAction( self ): if self .timer.isActive(): self .timer.stop() self .btn.setText( 'Start' ) else : self .timer.start( 100 , self ) self .btn.setText( 'Stop' ) |
在doAction()方法中,我们开始和停止定时器。
![QProgressBar](http://zetcode.com/img/gui/pyqt5/qprogressbar.png)
Figure: QProgressBar
日历组件(QCalendarWidget)
QCalendarWidget类提供了一个基于月的日历组件。它允许用户通过简单的直观的方式选择日期。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #!/usr/bin/python3 # -*- coding: utf-8 -*- """ ZetCode PyQt5 tutorial This example shows a QCalendarWidget widget. author: Jan Bodnar website: zetcode.com last edited: January 2015 """ import sys from PyQt5.QtWidgets import (QWidget, QCalendarWidget, QLabel, QApplication) from PyQt5.QtCore import QDate class Example(QWidget): def __init__( self ): super ().__init__() self .initUI() def initUI( self ): cal = QCalendarWidget( self ) cal.setGridVisible( True ) cal.move( 20 , 20 ) cal.clicked[QDate].connect( self .showDate) self .lbl = QLabel( self ) date = cal.selectedDate() self .lbl.setText(date.toString()) self .lbl.move( 130 , 260 ) self .setGeometry( 300 , 300 , 350 , 300 ) self .setWindowTitle( 'Calendar' ) self .show() def showDate( self , date): self .lbl.setText(date.toString()) if __name__ = = '__main__' : app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_()) |
以上的例子展示一个日历组件和标签组件。功能是日历中选择的日期会显示在标签组件中。
1 | cal = QCalendarWidget( self ) |
创建QCalendarWidget类。
1 | cal.clicked[QDate].connect( self .showDate) |
如果我们在组件上选择了一个日期,clicked[QDate]
信号会被发射。我们把这个信号和自定义的showDate()方法连接。
1 2 3 | def showDate( self , date): self .lbl.setText(date.toString()) |
我们通过selectedDate()方法检索被选中的日期。然后我们把选中的日期对象转化成字符串显示在标签组件上。