参考
《PyQT5快速开发与实战》
pyqt5官网:https://doc.qt.io/qtforpython
如果有许多的控件无法在一个页面中全部显示,可以考虑使用容器,装载更多的控件。
1. QTabWidget
QTabWidget控件提供了一个选项卡和一个页面区域(类似于平时看到的浏览器布局),默认显示第一个选项卡的页面。通过单击各个选项卡可以查看对应的页面。
QTabWidget常用方法:
表1
方法 | 描述 |
---|---|
addTab() | 将一个控件添加到Tab控件的选项卡中。 |
insertTab() | 将一个Tab控件的选项卡插入到指定的位置。 |
removeTab() | 根据指定的索引删除Tab控件。 |
setCurrentIndex() | 设置当前可见的选项卡所在的索引。 |
setCurrentWidget() | 设置当前可见的页面。 |
setTabBar() | 设置选项卡栏的小控件。 |
setTabPosition() | 设置选项卡的位置: QTabWidget.North :显示在页面的上方。 QTabWidget.South : 显示在页面的下方。 QTabWidget.West:显示在页面的左侧。 QTabWidget.Ease:显示在页面的右侧。 |
setTabText() | 定义Tab选项卡的显示值。 |
QTabWidget的常用信号:
表2
信号 | 描述 |
---|---|
currentChanged | 切换当前页面时发射信号。 |
示例代码1.
# -*- coding=utf-8 -*-
import sys
from PyQt5.QtWidgets import *
class TabDemo(QTabWidget): # 继承自QTabWidget
def __init__(self, parent=None):
super(TabDemo, self).__init__(parent)
self.resize(320, 240) # 窗口大小:宽320,高240
self.setTabPosition(QTabWidget.West) # 设置选项卡显示在页面的左侧
self.contact_tab = QWidget() # 每个选项卡界面其实都是一个QWidget
self.personal_details_tab = QWidget()
# “contact_tab”是对添加进去的contact_tab的描述
self.addTab(self.contact_tab, "contact_tab") # 添加“联系方式”的QWidget到QTabWidget中,成为选项卡
self.addTab(self.personal_details_tab, "personal_details_tab")
self.contact_tab_ui() # 绘制“联系方式”选项卡界面
self.personal_details_tab_ui() # 绘制“个人信息”选项卡界面
def contact_tab_ui(self):
layout = QFormLayout()
layout.addRow("姓名", QLineEdit())
layout.addRow("地址", QLineEdit())
self.setTabText(0, "联系方式") # 定义选项卡的显示值
self.contact_tab.setLayout(layout)
def personal_details_tab_ui(self):
layout = QFormLayout()
sex = QHBoxLayout()
sex.addWidget(QRadioButton("男"))
sex.addWidget(QRadioButton("女"))
layout.addRow(QLabel("性别"), sex)
layout.addRow("生日", QLineEdit())
self.setTabText(1, "个人详细信息")
self.personal_details_tab.setLayout(layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = TabDemo()
window.show()
sys.exit(app.exec_())
运行截图:
图1“联系方式”选项卡
图2“个人信息”选项卡
2. QStackedWidget
QStackedWidget是一个堆栈窗口控件,可以填充一些小控件,但是同一时间只能有一个小控件可以显示。
QStackedWidget使用QStackedLayout布局。
QStackedWidget和QTabWidget类似。
图3 QStackedWidget的继承关系
QStackedWidget的常用方法:
表3
方法 | 描述 |
---|---|
addWidget() | 添加widget到QStackedWidget中,返回索引位置。 |
count() | 返回QStackedWidget中widget的个数。 |
currentIndex() | 返回QStackedWidget中当前显示widget的索引。 |
currentWidget() | 返回QStackedWidget中当前显示的widget。 |
indexOf() | 返回指定widget在QStackedWidget中的索引。 |
insertWidget() | 在QStackedWidget中指定位置插入widget。 |
removeWidget() | 从QStackedWidget中删除指定widget(并没有实际删除,只是移除,相当于隐藏该widget) |
widget() | 返回指定索引位置的widget。 |
QStackedWidget的常用槽函数:
表4
槽 | 描述 |
---|---|
setCurrentIndex() | 设置当前要显示widget的索引。 |
setCurrentWidget() | 设置当前要显示的widget。 |
QStackedWidget的常用信号:
表5
信号 | 描述 |
---|---|
currentChanged() | 当前显示widget发生变化。 |
widgetRemoved() | widget被从QStackedWidget中移除。 |
示例代码2.
# -*-coding:utf-8 -*-
import sys
from PyQt5.QtWidgets import *
class StackedExample(QWidget):
def __init__(self):
super(StackedExample, self).__init__()
self.resize(320, 240)
self.setWindowTitle("StackedWidget例子")
self.left_list = QListWidget()
self.left_list.insertItem(0, '联系方式')
self.left_list.insertItem(1, '个人信息')
self.contact_stack = QWidget()
self.personal_detail_stack = QWidget()
self.contact_stack_ui()
self.personal_detail_stack_ui()
self.stack = QStackedWidget(self)
self.stack.addWidget(self.contact_stack)
self.stack.addWidget(self.personal_detail_stack)
hbox = QHBoxLayout(self)
hbox.addWidget(self.left_list)
hbox.addWidget(self.stack)
self.setLayout(hbox)
self.left_list.currentRowChanged.connect(self.display)
def contact_stack_ui(self):
layout = QFormLayout()
layout.addRow('姓名', QLineEdit())
layout.addRow('地址', QLineEdit())
self.contact_stack.setLayout(layout)
def personal_detail_stack_ui(self):
layout = QFormLayout()
sex = QHBoxLayout()
sex.addWidget(QRadioButton('男'))
sex.addWidget(QRadioButton('女'))
layout.addRow(QLabel('性别'), sex)
self.personal_detail_stack.setLayout(layout)
def display(self, i):
self.stack.setCurrentIndex(i)
if __name__ == "__main__":
app = QApplication(sys.argv)
demo = StackedExample()
demo.show()
sys.exit(app.exec_())
运行结果:
图4.“联系方式”界面
图5.“个人信息”界面
3. QDockWidget
QDockWidget是一个可以停靠在QMainWindow内的窗口控件,他可以保持在浮动状态或者在指定位置作为子窗口附加到主窗口中。
图6.QDockWidget的继承关系
图7. QMainWindow 结构图
QMainWindow类的主窗口对象保留有一个用于停靠窗口的区域,这个区域位于控件的中央周围,即(Dock Windows)。
表6.QDockWidget的常用方法
方法 | 描述 |
---|---|
setWidget() | 在Dock窗口区域设置QWidget. |
setFloating() | 设置Dock窗口是否可以浮动,如果设置为True,则表示可以浮动。 |
setAllowedAreas() | 设置窗口可以停靠的区域: 1.LeftDockWidgetArea,左边停靠区域。 2.RightDockWidgetArea,右边停靠区域。 3.TopDockWidgetArea,顶部停靠区域。 4.BottomDockWidgetArea,底部停靠区域。 5.NoDockWidgetArea,不显示Widget。 |
setFeatures() | 设置停靠窗口的功能属性: 1.DockWidgetClosable,可关闭。 2.DockWidgetMovable,可移动。 3.DockWidgetFloatable,可漂浮。 4.DockWidgetVerticalTitleBar,在左边显示垂直的标签栏。 5.AllDockWidgetFeatures,具有前3种属性的所有功能。 6.NoDockWidgetFeatures,无法关闭,无法移动,无法漂移。 |
示例代码3
# -*-coding:utf-8 -*-
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class DockExample(QMainWindow): # 顶层窗口是一个QMainWindow对象
def __init__(self, parent=None):
super(DockExample, self).__init__(parent)
self.resize(320, 240)
self.setWindowTitle("DockWidget例子")
layout = QHBoxLayout()
bar = self.menuBar()
file = bar.addMenu('Files')
file.addAction('New')
file.addAction('save')
file.addAction('quit')
self.items = QDockWidget('Dockable', self) # 创建可停靠的窗口
# 在停靠窗口items内添加QListWidget对象。
self.list_widget = QListWidget()
self.list_widget.addItem('item1')
self.list_widget.addItem('item2')
self.list_widget.addItem('item3')
self.items.setWidget(self.list_widget)
self.items.setFloating(False)
self.setCentralWidget(QTextEdit()) # QTextEdit对象是QMainWindow对象的中央小控件
# 将停靠窗口items放置在中央小控件的右侧。
self.addDockWidget(Qt.RightDockWidgetArea, self.items)
self.setLayout(layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
demo = DockExample()
demo.show()
sys.exit(app.exec_())
运行结果:
图8.Dockable窗口可以拖动
4.多文档界面
QTabWidget和QStackedWidget控件允许一次使用其中的一个窗口,不过其它窗口是隐藏的。
如果要同时显示多个窗口,就得创建多个独立的窗口,这需要消耗较多的内存资源。
独立的窗口称为SDI(single document interface,单文档界面)
包含这些SDI的窗口称为MDI(multiple document interface,多文档界面)。
MDI应用程序占用较少的内存资源,相比于同时创建多个窗口,MDI的子窗口都可以放在主窗口容器内。该主窗口容器称为QMdiArea。
QMdiArea控件一般放在QMainWindow对象的中央位置,其子窗口在这个区域内是QMdiSubWindow类的实例,,可以设置任何QWidget作为子窗口对象的内部控件,子窗口在MDI区域进行级联排列布局。
图9.QMdiArea类的继承关系
图10.QMdiSubWindow的继承关系
图11.多文档界面
QMdiArea类和QMdiSubWindow类的常用方法:
更多信息得看官网。
表7
方法 | 描述 |
---|---|
addSubWindow() | 将一个小控件添加在MDI区域作为一个新的子窗口。 |
removeSubWindow() | 删除一个子窗口中的小控件。 |
setActiveSubWindow() | 激活一个子窗口。 |
cascadeSubWindow() | 安排子窗口在MDI区域级联显示。 |
tileSubWindows() | 安排子窗口在MDI区域平铺显示。 |
closeActiveSubWindow() | 关闭活动的窗口。 |
subWindowList() | 返回MDI区域的子窗口列表。 |
setWidget() | 设置一个小控件作为QMdiSubWindow实例对象的内部控件。 |
代码示例4:
# -*-coding:utf-8 -*-
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow): # 顶层窗口是一个QMainWindow对象
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(320, 240)
self.setWindowTitle("多文档界面例子")
self.count = 0 # 记录子窗口的数量
self.mdi = QMdiArea()
self.setCentralWidget(self.mdi)
bar = self.menuBar()
file = bar.addMenu('File')
file.addAction('New')
file.addAction('cascade')
file.addAction('Tiled')
# 单击菜单控件时触发triggered信号,连接到槽函数window_action
file.triggered[QAction].connect(self.window_action)
def window_action(self, action):
print('triggered')
if action.text() == 'New': # 添加一个新的SDI,每个SDI都有标题,主窗口内会增加SDI数量
self.count = self.count + 1
sub = QMdiSubWindow()
sub.setWidget(QTextEdit())
sub.setWindowTitle('sub window' + str(self.count))
self.mdi.addSubWindow(sub)
sub.show()
elif action.text() == 'cascade': # 级联显示子窗口
self.mdi.cascadeSubWindows()
elif action.text() == 'Tiled': # 平铺显示子窗口
self.mdi.tileSubWindows()
if __name__ == "__main__":
app = QApplication(sys.argv)
demo = MainWindow()
demo.show()
sys.exit(app.exec_())
运行结果:
图12.点击New,创建新窗口
图13.子窗口的级联布局
图14.子窗口的平铺布局
5. QScrollBar
前4个窗口控件的共同点:新建一些窗口来装在更多的控件。
QScrollBar:提供水平/垂直的滚动条,扩大当前窗口的有效装载面积,从而装载更多控件。
图15. QScrollBar的继承关系
表8. QScrollBar常用信号
信号 | 含义 |
---|---|
valueChanged() | 当滑动条的值改变时发射此信号。 |
sliderMoved() | 当用户拖动滑块时发射此信号。 |
示例代码5:
# -*-coding:utf-8 -*-
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class ScrollExample(QWidget):
def __init__(self, parent=None):
super(ScrollExample, self).__init__(parent)
self.resize(320, 240)
self.setWindowTitle("滚动条例子")
self.scroll_bar = QScrollBar(self)
self.scroll_bar.resize(20, 240)
self.scroll_bar.valueChanged.connect(self.slider_slot)
self.pushbutton = QPushButton('测试', self)
self.pushbutton.setGeometry(40, 0, 40, 30)
def slider_slot(self, value):
self.pushbutton.move(40, value)
if __name__ == "__main__":
app = QApplication(sys.argv)
demo = ScrollExample()
demo.show()
sys.exit(app.exec_())
运行结果:
图16.拖动滚动条,“测试”按钮一起移动