一、表格与树
表格与树解决的问题是如何在一个控件中有规律地呈现更多的数据。解决该问题的两种控件类为表格结构的控件类,树形结构的控件类。
1、QTableView
若一个应用和一批数据(如数组、列表)进行交互后要以表格的形式输出这些信息的话,就用QTableView类。在QtableView中可以使用自定义的数据模型来显示内容,通过setModel来绑定数据源,可用的模式如下:
QStringListModel:存储一组字符串
QStandardItemModel:存储任意层次结构的数据
QDirModel:对文件系统进行封装
QSqlQueryModel:对SQL的查询结构集进行封装
QSqlTableModel:对SQL中的表格进行封装
QSqlRelationalTableModel:对带有foreign key的SQL表格进行封装
QSortFilterProxyModel:对模型中的数据进行排序或过滤
QTableView的使用例子:
# -*- coding: utf-8 -*- | |
''' | |
【简介】 | |
PyQT5中QTableView表格视图控件的例子 | |
''' | |
from PyQt5.QtWidgets import * | |
from PyQt5.QtGui import * | |
from PyQt5.QtCore import * | |
import sys | |
class Table(QWidget): | |
def __init__(self, arg=None): | |
super(Table, self).__init__(arg) | |
self.setWindowTitle("QTableView表格视图控件的例子") | |
self.resize(500,300); | |
self.model=QStandardItemModel(4,4); | |
self.model.setHorizontalHeaderLabels(['标题1','标题2','标题3','标题4']) | |
for row in range(4): | |
for column in range(4): | |
item = QStandardItem("row %s, column %s"%(row,column)) | |
self.model.setItem(row, column, item) | |
self.tableView=QTableView() | |
self.tableView.setModel(self.model) | |
#下面代码让表格100填满窗口 | |
#self.tableView.horizontalHeader().setStretchLastSection(True) | |
#self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) | |
dlgLayout=QVBoxLayout(); | |
dlgLayout.addWidget(self.tableView) | |
self.setLayout(dlgLayout) | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
table = Table() | |
table.show() | |
sys.exit(app.exec_()) |
运行结果:
2、QListView
常用方法:setModel():用来设置View所关联的Model,可以使用Python原生的list作为数据源Model
selectedItem():选中Model中的条目
isSelected():判断Model中的某条条目是否被选中
常用信号:clicked:当单击某项时,信号被发射
doubleClicked:当双击某项时,信号被发射
使用QListView控件例子如下:
# -*- coding: utf-8 -*- | |
''' | |
【简介】 | |
PyQt5中 QListView 例子 | |
''' | |
from PyQt5.QtWidgets import QApplication, QWidget , QVBoxLayout , QListView, QMessageBox | |
from PyQt5.QtCore import QStringListModel | |
import sys | |
class ListViewDemo(QWidget): | |
def __init__(self, parent=None): | |
super(ListViewDemo, self).__init__(parent) | |
self.setWindowTitle("QListView 例子") | |
self.resize(300, 270) | |
layout = QVBoxLayout() | |
listView = QListView() | |
slm = QStringListModel(); | |
self.qList = ['Item 1','Item 2','Item 3','Item 4' ] | |
slm.setStringList(self.qList) | |
listView.setModel(slm ) | |
listView.clicked.connect(self.clicked) | |
layout.addWidget( listView ) | |
self.setLayout(layout) | |
def clicked(self, qModelIndex): | |
QMessageBox.information(self, "QListView", "你选择了: "+ self.qList[qModelIndex.row()]) | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
win = ListViewDemo() | |
win.show() | |
sys.exit(app.exec_()) |
运行结果:
3、QListWidget
它是一个基于条目的接口,用于从列表中添加或删除条目。列表中的每个条目都是一个QListWidgetItem对象。QListWidget可以设置为多重选择。
QListWidget类中的常用方法为:
addItem():在列表中添加QListWidgetItem对象或字符串
addItems():添加列表中的每个条目
insertItem():在指定的索引处插入条目
clear():删除列表的内容
setCurrentItem():设置当前所选条目
sortItems():按升序重新排列条目
常用信号为:currentItemChanged:当列表中的条目发生改变时发射此信号
itemClicked:当点击列表中的条目时发射此信号
QListWidget使用例子:
# -*- coding: utf-8 -*- | |
''' | |
【简介】 | |
PyQt5中 QListWidget 例子 | |
''' | |
import sys | |
from PyQt5.QtCore import * | |
from PyQt5.QtGui import * | |
from PyQt5.QtWidgets import * | |
class ListWidget(QListWidget): | |
def clicked(self,item): | |
QMessageBox.information(self, "ListWidget", "你选择了: "+item.text()) | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
listWidget = ListWidget() | |
listWidget.resize(300,120) | |
listWidget.addItem("Item 1"); | |
listWidget.addItem("Item 2"); | |
listWidget.addItem("Item 3"); | |
listWidget.addItem("Item 4"); | |
listWidget.setWindowTitle('QListwidget 例子') | |
listWidget.itemClicked.connect(listWidget.clicked) | |
listWidget.show() | |
sys.exit(app.exec_()) |
运行结果:
4、QTableWidget
它是Qt程序中常用的显示数据表格的控件。
QTableWidget基本用法例子:
# -*- coding: utf-8 -*- | |
''' | |
【简介】 | |
PyQT5中单元格的基本例子 | |
''' | |
import sys | |
from PyQt5.QtWidgets import (QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem, QAbstractItemView ) | |
class Table(QWidget): | |
def __init__(self): | |
super().__init__() | |
self.initUI() | |
def initUI(self): | |
self.setWindowTitle("QTableWidget 例子") | |
self.resize(430,230); | |
conLayout = QHBoxLayout() | |
tableWidget = QTableWidget() | |
tableWidget.setRowCount(4) | |
tableWidget.setColumnCount(3) | |
conLayout.addWidget(tableWidget ) | |
tableWidget.setHorizontalHeaderLabels(['姓名','性别','体重(kg)']) | |
newItem = QTableWidgetItem("张三") | |
tableWidget.setItem(0, 0, newItem) | |
newItem = QTableWidgetItem("男") | |
tableWidget.setItem(0, 1, newItem) | |
newItem = QTableWidgetItem("160") | |
tableWidget.setItem(0, 2, newItem) | |
# 将表格变为禁止编辑 | |
#tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) | |
# 设置表格为整行选择 | |
#tableWidget.setSelectionBehavior( QAbstractItemView.SelectRows) | |
# 将行和列的大小设为与内容相匹配 | |
#tableWidget.resizeColumnsToContents() | |
#tableWidget.resizeRowsToContents() | |
#表格表头的显示与隐藏 | |
#tableWidget.verticalHeader().setVisible(False) | |
#tableWidget.horizontalHeader().setVisible(False) | |
# 不显示表格单元格的分割线 | |
#tableWidget.setShowGrid(False) | |
# 不显示垂直表头 | |
tableWidget.verticalHeader().setVisible(False) | |
self.setLayout(conLayout) | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
example = Table() | |
example.show() | |
sys.exit(app.exec_()) |
运行结果:
5、QTreeView
它实现了树形结构,其中QTreeWidget常用方法为:
setColumnWidth(int column,int width):将指定列的宽度设置为给定的值
insertTopLevelItems():在视图的顶层索引中插入项目列表
expandAll():展开所有的树形节点
invisibleRootItem():返回树形控件中不可见的根选项(Root Item)
selectedItems():返回所有选定的非隐藏项目的列表
QTreeWidgetItem类中的常用方法为:
addChild():将子项追加到子列表中
setText():设置显示的节点文本
Text():返回显示的节点文本
setCheckState(column,state):设置指定列的选中状态
setIcon(column,icon):在指定的列中显示图标
树形结构实现的例子:
''' | |
【简介】 | |
PyQT5中 QTreeWidget 例子 | |
''' | |
import sys | |
from PyQt5.QtWidgets import * | |
from PyQt5.QtGui import QIcon , QBrush , QColor | |
from PyQt5.QtCore import Qt | |
class TreeWidgetDemo(QMainWindow): | |
def __init__(self,parent=None): | |
super(TreeWidgetDemo,self).__init__(parent) | |
self.setWindowTitle('TreeWidget 例子') | |
self.tree = QTreeWidget() | |
# 设置列数 | |
self.tree.setColumnCount(2) | |
# 设置头的标题 | |
self.tree.setHeaderLabels(['Key','Value']) | |
# 设置根节点 | |
root= QTreeWidgetItem(self.tree) | |
root.setText(0,'root') | |
root.setIcon(0,QIcon("./images/root.png")) | |
# 设置列宽 | |
self.tree.setColumnWidth(0, 160) | |
### 设置节点的背景颜色 | |
#brush_red = QBrush(Qt.red) | |
#root.setBackground(0, brush_red) | |
#brush_green = QBrush(Qt.green) | |
#root.setBackground(1, brush_green) | |
# 设置子节点1 | |
child1 = QTreeWidgetItem(root) | |
child1.setText(0,'child1') | |
child1.setText(1,'ios') | |
child1.setIcon(0,QIcon("./images/IOS.png")) | |
child1.setCheckState(0, Qt.Checked) | |
# 设置子节点2 | |
child2 = QTreeWidgetItem(root) | |
child2.setText(0,'child2') | |
child2.setText(1,'') | |
child2.setIcon(0,QIcon("./images/android.png")) | |
# 设置子节点3 | |
child3 = QTreeWidgetItem(child2) | |
child3.setText(0,'child3') | |
child3.setText(1,'android') | |
child3.setIcon(0,QIcon("./images/music.png")) | |
self.tree.addTopLevelItem(root) | |
# 结点全部展开 | |
self.tree.expandAll() | |
self.setCentralWidget(self.tree) | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
tree = TreeWidgetDemo() | |
tree.show() | |
sys.exit(app.exec_()) |
运行结果:
二、容器:装载更多的控件
1、QTabWidget
它提供了一个选项卡和一个页面区域,默认显示第一个选项卡的页面。通过单击各选项卡可以查看对应的页面。常用方法:
addTab():将一个控件添加到Tab控件的选项卡中
insertTab():将一个Tab控件的选项卡插入到指定的位置
removeTab():根据指定的索引删除Tab控件
setCurrentIndex():设置当前可见的选项卡所在的索引
setCurrentWidget():设置当前可见的页面
setTabBar():设置选项卡栏的小控件
setTabPosition():设置选项卡的位置 QTabWidget.North 显示在页面上方 QTabWidget.South 显示在页面下方,左右同理
setTabText():定义Tab选项卡的显示值
例子:
''' | |
【简介】 | |
PyQt5中 QTabWidget 例子 | |
''' | |
import sys | |
from PyQt5.QtCore import * | |
from PyQt5.QtGui import * | |
from PyQt5.QtWidgets import * | |
class TabDemo(QTabWidget): | |
def __init__(self, parent=None): | |
super(TabDemo, self).__init__(parent) | |
self.tab1 = QWidget() | |
self.tab2 = QWidget() | |
self.tab3 = QWidget() | |
self.addTab(self.tab1,"Tab 1") | |
self.addTab(self.tab2,"Tab 2") | |
self.addTab(self.tab3,"Tab 3") | |
self.tab1UI() | |
self.tab2UI() | |
self.tab3UI() | |
self.setWindowTitle("Tab 例子") | |
def tab1UI(self): | |
layout = QFormLayout() | |
layout.addRow("姓名",QLineEdit()) | |
layout.addRow("地址",QLineEdit()) | |
self.setTabText(0,"联系方式") | |
self.tab1.setLayout(layout) | |
def tab2UI(self): | |
layout = QFormLayout() | |
sex = QHBoxLayout() | |
sex.addWidget(QRadioButton("男")) | |
sex.addWidget(QRadioButton("女")) | |
layout.addRow(QLabel("性别"),sex) | |
layout.addRow("生日",QLineEdit()) | |
self.setTabText(1,"个人详细信息") | |
self.tab2.setLayout(layout) | |
def tab3UI(self): | |
layout=QHBoxLayout() | |
layout.addWidget(QLabel("科目")) | |
layout.addWidget(QCheckBox("物理")) | |
layout.addWidget(QCheckBox("高数")) | |
self.setTabText(2,"教育程度") | |
self.tab3.setLayout(layout) | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
demo = TabDemo() | |
demo.show() | |
sys.exit(app.exec_()) |
运行结果:
2、QStackedWidget
它是一个堆栈窗口的控件,可以填充一些小的控件,但同一时间只有一个小控件可以显示。QStackedWidget使用QStackedLayout布局。QStackedWidget控件与QTabWidget类似,可以有效地显示窗口中的控件。
例子:
''' | |
【简介】 | |
PyQt5中 QStackedWidget 例子 | |
''' | |
import sys | |
from PyQt5.QtCore import * | |
from PyQt5.QtGui import * | |
from PyQt5.QtWidgets import * | |
class StackedExample(QWidget): | |
def __init__(self): | |
super(StackedExample, self).__init__() | |
self.setGeometry(300, 50, 10,10) | |
self.setWindowTitle('StackedWidget 例子') | |
self.leftlist = QListWidget () | |
self.leftlist.insertItem (0, '联系方式' ) | |
self.leftlist.insertItem (1, '个人信息' ) | |
self.leftlist.insertItem (2, '教育程度' ) | |
self.stack1= QWidget() | |
self.stack2= QWidget() | |
self.stack3= QWidget() | |
self.stack1UI() | |
self.stack2UI() | |
self.stack3UI() | |
self.Stack = QStackedWidget (self) | |
self.Stack.addWidget (self.stack1) | |
self.Stack.addWidget (self.stack2) | |
self.Stack.addWidget (self.stack3) | |
hbox = QHBoxLayout(self) | |
hbox.addWidget(self.leftlist) | |
hbox.addWidget(self.Stack) | |
self.setLayout(hbox) | |
self.leftlist.currentRowChanged.connect(self.display) | |
def stack1UI(self): | |
layout=QFormLayout() | |
layout.addRow("姓名",QLineEdit()) | |
layout.addRow("地址",QLineEdit()) | |
self.stack1.setLayout(layout) | |
def stack2UI(self): | |
layout=QFormLayout() | |
sex=QHBoxLayout() | |
sex.addWidget(QRadioButton("男")) | |
sex.addWidget(QRadioButton("女")) | |
layout.addRow(QLabel("性别"),sex) | |
layout.addRow("生日",QLineEdit()) | |
self.stack2.setLayout(layout) | |
def stack3UI(self): | |
layout=QHBoxLayout() | |
layout.addWidget(QLabel("科目")) | |
layout.addWidget(QCheckBox("物理")) | |
layout.addWidget(QCheckBox("高数")) | |
self.stack3.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_()) |
运行结果:
3、QDockWidget
它是一个可以停靠在QMainWindow内的窗口控件,它可以保持在浮动状态或者在指定位置作为子窗口附加到主窗口中。QMainWindow类的主窗口对象保留一个用于停靠窗口的区域,这个区域在控件的*周围,QDockWidget控件在主窗口内可以移动到新的区域。QDockWidget类中的常用方法为:
setWidget():在Dock窗口区域设置QWidget
setFloating():设置Dock窗口是否可以浮动,如果设置为true,则表示可以浮动
setAllowedAreas():设置窗口可以停靠的区域: LeftDockWidgetArea 左边停靠区域
RightDockWidgetArea 右边停靠区域
TopDockWidgetArea 顶部停靠区域
BottomDockWidgetArea 底部停靠区域
NoDockWidgetArea 不显示Widget
setFeatures():设置停靠窗口的功能属性: DockWidgetClosable 可关闭
DockWidgetMovable 可移动
DockWidgetFloatable 可漂浮
DockWidgetVerticalTitleBar 在左边显示垂直的标签栏
AllDockWidgetFeatures 具有前三种属性的所有功能
NoDockWidgetFeatures 无法关闭,不能移动,不能漂浮
QDockWidget使用例子:
''' | |
【简介】 | |
PyQt5中 QDockWidget 例子 | |
''' | |
import sys | |
from PyQt5.QtCore import * | |
from PyQt5.QtGui import * | |
from PyQt5.QtWidgets import * | |
class DockDemo(QMainWindow): | |
def __init__(self, parent=None): | |
super(DockDemo, self).__init__(parent) | |
layout = QHBoxLayout() | |
bar=self.menuBar() | |
file=bar.addMenu("File") | |
file.addAction("New") | |
file.addAction("save") | |
file.addAction("quit") | |
self.items = QDockWidget("Dockable", self) | |
self.listWidget = QListWidget() | |
self.listWidget.addItem("item1") | |
self.listWidget.addItem("item2") | |
self.listWidget.addItem("item3") | |
self.items.setWidget(self.listWidget) | |
self.items.setFloating(False) | |
self.setCentralWidget(QTextEdit()) | |
self.addDockWidget(Qt.RightDockWidgetArea, self.items) | |
self.setLayout(layout) | |
self.setWindowTitle("Dock 例子") | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
demo = DockDemo() | |
demo.show() | |
sys.exit(app.exec_()) |
4、多文档界面
MDI(Multiple Document Interface,多文档界面)应用程序占用较少的内存资源,子窗口都可以放在主窗口容器中,这个容器控件被称为QMdiArea。QMdiArea控件通常占据在QMainWindow对象的中间位置,子窗口在这个区域是QMdiSubWindow类的实例,可以设置任何QWidget作为子窗口对象的内部控件,QMdiArea类和QMdiSubWindow类中的常用方法如下:
addSubWindow():将一个小控件添加在MDI区域作为一个新的子窗口
removeSubWindow():删除一个子窗口的小控件
setActiveSubWindow():激活一个子窗口
cascadeSubWindows():安排子窗口在MDI区域级联显示
tileSubWindows():安排子窗口在MDI区域平铺显示
closeActiveSubWindow():关闭活动的子窗口
subWindowList():返回MDI区域的子窗口列表
setWidget():设置一个小控件作为QMdiSubwindow实例对象的内部控件
例子:
''' | |
【简介】 | |
PyQt5中 QMdiArea 例子 | |
''' | |
import sys | |
from PyQt5.QtCore import * | |
from PyQt5.QtGui import * | |
from PyQt5.QtWidgets import * | |
class MainWindow(QMainWindow): | |
count=0 | |
def __init__(self, parent=None): | |
super(MainWindow, self).__init__(parent) | |
self.mdi = QMdiArea() | |
self.setCentralWidget(self.mdi) | |
bar=self.menuBar() | |
file=bar.addMenu("File") | |
file.addAction("New") | |
file.addAction("cascade") | |
file.addAction("Tiled") | |
file.triggered[QAction].connect(self.windowaction) | |
self.setWindowTitle("MDI demo") | |
def windowaction(self, q): | |
print( "triggered") | |
if q.text()=="New": | |
MainWindow.count=MainWindow.count+1 | |
sub=QMdiSubWindow() | |
sub.setWidget(QTextEdit()) | |
sub.setWindowTitle("subwindow"+str(MainWindow.count)) | |
self.mdi.addSubWindow(sub) | |
sub.show() | |
if q.text()=="cascade": | |
self.mdi.cascadeSubWindows() | |
if q.text()=="Tiled": | |
self.mdi.tileSubWindows() | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
demo = MainWindow() | |
demo.show() | |
sys.exit(app.exec_()) | |
运行结果:
5、QScrollBar
提供了水平或者垂直的滚动条,这样可以扩大当前窗口的有效装载面积,从而装载更多的控件。常用方法:
valueChanged:当滑动条的值改变时发射该信号
sliderMoved:当用户拖动滑动条时发射此信号
例子:
''' | |
【简介】 | |
PyQt5中 QScrollBar 例子 | |
''' | |
import sys | |
from PyQt5.QtCore import * | |
from PyQt5.QtGui import * | |
from PyQt5.QtWidgets import * | |
class Example(QWidget): | |
def __init__(self): | |
super(Example, self).__init__() | |
self.initUI() | |
def initUI(self): | |
hbox = QHBoxLayout( ) | |
self.l1 = QLabel("拖动滑动条去改变颜色") | |
self.l1.setFont(QFont("Arial",16)) | |
hbox.addWidget(self.l1) | |
self.s1 = QScrollBar() | |
self.s1.setMaximum(255) | |
self.s1.sliderMoved.connect(self.sliderval) | |
self.s2 = QScrollBar() | |
self.s2.setMaximum(255) | |
self.s2.sliderMoved.connect(self.sliderval) | |
self.s3 = QScrollBar() | |
self.s3.setMaximum(255) | |
self.s3.sliderMoved.connect(self.sliderval) | |
hbox.addWidget(self.s1) | |
hbox.addWidget(self.s2) | |
hbox.addWidget(self.s3) | |
self.setGeometry(300, 300, 300, 200) | |
self.setWindowTitle('QScrollBar 例子') | |
self.setLayout( hbox ) | |
def sliderval(self): | |
print( self.s1.value(),self.s2.value(), self.s3.value() ) | |
palette = QPalette() | |
c = QColor(self.s1.value(),self.s2.value(), self.s3.value(),255) | |
palette.setColor(QPalette.Foreground,c) | |
self.l1.setPalette(palette) | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
demo = Example() | |
demo.show() | |
sys.exit(app.exec_()) |
运行结果:
三、多线程
多线程技术涉及三种方法,一种是使用计时器模块QTimer;一种是使用多线程模块QThread;还有一种是使用事件处理的功能。
1、QTimer
如果要在应用程序中周期性地进行某项操作,比如周期性地检查主机的CPU值,则需要用到QTimer(定时器),QTimer类提供了重复的和单次的定时器。要使用定时器,需要创建一个QTimer实例,将其timeout信号连接到相应的槽,并调用start()。然后,定时器会以恒定的间隔发出timeout信号。当窗口控件收到timeout信号后,它就会停止这个定时器。这是在图形用户界面中实现复制工作的一个典型方法,随着技术的进步,多线程在越来越多的平台上被使用,最终QTimer对象会被线程所替代。
QTimer类中的常用方法: start(milliseconds):启动或重启定时器,时间间隔为毫秒。如果定时器已经运行,它将被停止并重新启动。如果singleShot信号为真,定时器将仅被激活一次
Stop():停止定时器
QTimer类中的常用信号:singleShot:在给定的时间间隔后调用一个槽函数时发射此信号
timeout:当定时器超时时发射此信号
例子:
''' | |
【简介】 | |
PyQT5中 QTimer例子 | |
''' | |
from PyQt5.QtWidgets import QWidget, QPushButton , QApplication ,QListWidget, QGridLayout , QLabel | |
from PyQt5.QtCore import QTimer ,QDateTime | |
import sys | |
class WinForm(QWidget): | |
def __init__(self,parent=None): | |
super(WinForm,self).__init__(parent) | |
self.setWindowTitle("QTimer demo") | |
self.listFile= QListWidget() | |
self.label = QLabel('显示当前时间') | |
self.startBtn = QPushButton('开始') | |
self.endBtn = QPushButton('结束') | |
layout = QGridLayout(self) | |
# 初始化一个定时器 | |
self.timer = QTimer(self) | |
# showTime()方法 | |
self.timer.timeout.connect(self.showTime) | |
layout.addWidget(self.label,0,0,1,2) | |
layout.addWidget(self.startBtn,1,0) | |
layout.addWidget(self.endBtn,1,1) | |
self.startBtn.clicked.connect( self.startTimer) | |
self.endBtn.clicked.connect( self.endTimer) | |
self.setLayout(layout) | |
def showTime(self): | |
# 获取系统现在的时间 | |
time = QDateTime.currentDateTime() | |
# 设置系统时间显示格式 | |
timeDisplay = time.toString("yyyy-MM-dd hh:mm:ss dddd"); | |
# 在标签上显示时间 | |
self.label.setText( timeDisplay ) | |
def startTimer(self): | |
# 设置计时间隔并启动 | |
self.timer.start(1000) | |
self.startBtn.setEnabled(False) | |
self.endBtn.setEnabled(True) | |
def endTimer(self): | |
self.timer.stop() | |
self.startBtn.setEnabled(True) | |
self.endBtn.setEnabled(False) | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
form = WinForm() | |
form.show() | |
sys.exit(app.exec_()) |
运行结果:
例二:
''' | |
【简介】 | |
PyQT5中关闭应用例子 | |
''' | |
import sys | |
from PyQt5.QtWidgets import * | |
from PyQt5.QtGui import * | |
from PyQt5.QtCore import * | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
label = QLabel("<font color=red size=128><b>Hello PyQT,窗口会在10秒后消失!</b></font>") | |
label.setWindowFlags(Qt.SplashScreen|Qt.FramelessWindowHint) | |
label.show() | |
# 设置10s后自动退出 | |
QTimer.singleShot(10000, app.quit) | |
sys.exit(app.exec_()) |
运行结果:
2、QThread
它是Qt线程类中最核心的底层类。要使用QThread开始一个线程,可以创建它的一个子类,然后覆盖其QThread.run()函数;PyQt的线程使用非常简单-------建立一个自定义的类(如Thread),使它继承自QThread,并实现其run()方法即可。
在使用线程时可以直接得到Thread实例,调用其start()函数即可启动线程。线程启动之后,会自动调用其实现的run方法,该方法就是线程的执行函数。
业务的线程任务就写在run()函数中,当run()推出之后线程基本就结束了。QThread有started和finished信号,可以为这两个信号指定槽函数,在线程启动和结束时执行一段代码进行资源的初始化和释放操作。更灵活的使用方法是,在自定义的QThread实例中自定义信号,并将信号连接到指定的槽函数,当满足一定条件后发射该信号。
QThread类中的常用方法:
start():启动线程
wait():阻止线程
sleep():强制当前线程睡眠
QThread类中的常用信号:
started:在开始执行run()函数之前,从相关线程发射该信号
finished:当程序完成业务逻辑时,从相关线程发射该信号
QThread实例:当在窗口中显示的数据比较简单时,可以把读取数据的业务逻辑放在窗口的初始化代码中;但如果读取数据的时间比较长,比如网络请求数据的时间比较长,则可以把这部分逻辑放在QThread线程中,实现界面的数据显示和数据读取的分离,以满足MVC(模型---视图---控制器)设计模式的要求。
例子:
''' | |
【简介】 | |
PyQT5中 QThread 例子 | |
''' | |
from PyQt5.QtCore import * | |
from PyQt5.QtGui import * | |
from PyQt5.QtWidgets import * | |
import sys | |
class MainWidget(QWidget): | |
def __init__(self,parent=None): | |
super(MainWidget,self).__init__(parent) | |
self.setWindowTitle("QThread 例子") | |
self.thread = Worker() | |
self.listFile = QListWidget() | |
self.btnStart = QPushButton('开始') | |
layout = QGridLayout(self) | |
layout.addWidget(self.listFile,0,0,1,2) | |
layout.addWidget(self.btnStart,1,1) | |
self.btnStart.clicked.connect( self.slotStart ) | |
self.thread.sinOut.connect(self.slotAdd) | |
def slotAdd(self,file_inf): | |
self.listFile.addItem(file_inf) | |
def slotStart(self): | |
self.btnStart.setEnabled(False) | |
self.thread.start() | |
class Worker(QThread): | |
sinOut = pyqtSignal(str) | |
def __init__(self,parent=None): | |
super(Worker,self).__init__(parent) | |
self.working = True | |
self.num = 0 | |
def __del__(self): | |
self.working = False | |
self.wait() | |
def run(self): | |
while self.working == True: | |
file_str = 'File index {0}'.format(self.num) | |
self.num += 1 | |
# 发出信号 | |
self.sinOut.emit(file_str) | |
# 线程休眠2秒 | |
self.sleep(2) | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
demo = MainWidget() | |
demo.show() | |
sys.exit(app.exec_()) |
运行结果:
但是在PyQt中所有的窗口都在UI主线程中(就是执行了QApplication.exec()的线程),在这个线程中执行耗时操作会阻塞UI线程,从而让窗口停止响应。如果窗口长时间没有响应,则会影响用户体验,更严重的会导致程序崩溃。所以,为了避免出现这样的问题,要使用QThread开启一个新的线程,在这个线程中完成耗时的操作。例子如下:
''' | |
【简介】 | |
PyQT5中 QTimer例子 | |
''' | |
import sys | |
from PyQt5.QtCore import * | |
from PyQt5.QtGui import * | |
from PyQt5.QtWidgets import * | |
global sec | |
sec=0 | |
class WorkThread(QThread): | |
trigger = pyqtSignal() | |
def __int__(self): | |
super(WorkThread,self).__init__() | |
def run(self): | |
for i in range(2000000000): | |
pass | |
# 循环完毕后发出信号 | |
self.trigger.emit() | |
def countTime(): | |
global sec | |
sec += 1 | |
# LED显示数字+1 | |
lcdNumber.display(sec) | |
def work(): | |
# 计时器每秒计数 | |
timer.start(1000) | |
# 计时开始 | |
workThread.start() | |
# 当获得循环完毕的信号时,停止计数 | |
workThread.trigger.connect(timeStop) | |
def timeStop(): | |
timer.stop() | |
print("运行结束用时",lcdNumber.value()) | |
global sec | |
sec=0 | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
top = QWidget() | |
top.resize(300,120) | |
# 垂直布局类QVBoxLayout | |
layout = QVBoxLayout(top) | |
# 加个显示屏 | |
lcdNumber = QLCDNumber() | |
layout.addWidget(lcdNumber) | |
button = QPushButton("测试") | |
layout.addWidget(button) | |
timer = QTimer() | |
workThread = WorkThread() | |
button.clicked.connect(work) | |
# 每次计时结束,触发 countTime | |
timer.timeout.connect(countTime) | |
top.show() | |
sys.exit(app.exec_()) |
运行结果:
3、事件处理
PyQt为事件处理提供了两种机制:高级的信号与槽机制,以及低级的事件处理程序。今天就学低级的事件处理程序,也就是processEvents()函数的使用方法,它的作用是处理事件,简单地说,就是刷新页面。
对应执行很耗时的程序来说,由于PyQt需要等待程序执行完毕才能进行下一步,这个过程表现在界面上就是卡顿;而如果在执行这个耗时程序时不断地运行QApplication.processEvents(),那么就可以实现一边执行耗时程序,一边刷新页面的功能,给人的感觉就是程序运行很流畅。因此QApplication.processEvents()的使用方法就是,在主函数执行耗时操作的地方,加入QApplication.processEvents().例子如下:
''' | |
【简介】 | |
PyQT5中实时刷新界面例子 | |
''' | |
from PyQt5.QtWidgets import QWidget, QPushButton , QApplication ,QListWidget, QGridLayout | |
import sys | |
import time | |
class WinForm(QWidget): | |
def __init__(self,parent=None): | |
super(WinForm,self).__init__(parent) | |
self.setWindowTitle("实时刷新界面例子") | |
self.listFile= QListWidget() | |
self.btnStart = QPushButton('开始') | |
layout = QGridLayout(self) | |
layout.addWidget(self.listFile,0,0,1,2) | |
layout.addWidget(self.btnStart,1,1) | |
self.btnStart.clicked.connect( self.slotAdd) | |
self.setLayout(layout) | |
def slotAdd(self): | |
for n in range(10): | |
str_n='File index {0}'.format(n) | |
self.listFile.addItem(str_n) | |
QApplication.processEvents() | |
time.sleep(1) | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
form = WinForm() | |
form.show() | |
sys.exit(app.exec_()) |
运行结果:
四、网页交互
略~