一、PYQT的环境搭建:
系统环境:centos7
开发环境:PYQT5.6
1.安装CentOS7
1)安装完成后进行更新
sudo yum update -y
2)增加epel 源
yum install epel-release -y
2.安装qt5
1)安装qt5.6
yum install qt5-qtquickcontrols qt5-qtdeclarative-devel -y
3.安装PyQT5
1)安装编译环境
yum install gcc gcc-c++ python-devel -y
2)下载sip-4.18
wget http://nchc.dl.sourceforge.net/project/pyqt/sip/sip-4.18/sip-4.18.tar.gz
3)装sip-4.18
sudo tar xzvf sip-4.18.tar.gz
sudo cd sip
sudo python configure.py
sudo make
sudo make install
4)下载pyqt5.6
wget http://nchc.dl.sourceforge.net/project/pyqt/PyQt5/PyQt-5.6/PyQt5_gpl-5.6.tar.gz
5)安装qt5-qtbase-devel
yum install qt5-qtbase-devel -y
6)编译并安装 pyqt
sudo tar xzvf PyQt5_gpl-5.6.tar.gz
sudo cd PyQt5_gpl-5.6
sudo python configure.py --qmake=/usr/bin/qmake-qt5
sudo make all
sudo make install
4.异常处理
1)步骤sudo python configure.py --qmake=/usr/bin/qmake-qt5会出错:
Error: This version of PyQt5 and the commercial version of Qt have incompatible licenses.
解决办法:
vi configure.py #大约在2681行,可根据关键字搜索修改斜体标记处: if introspecting and target_config.qt_licensee not in ‘Open Source’ and ltype == ‘GPL’:
2) 步骤sudo make all 会出错:
error: ‘WindowOkButtonHint’ : is not a member of ‘Qt’
error: ‘WindowCancelButtonHint’ : is not a member of ‘Qt’
解决办法:根绝报错大约是在/root/PyQt5_gpl-5.6/QtCore/sipQtCoreQt.cpp 的1085和1098行, 删除这两行就好了
二、PYQT编译方式:
这里共整理两种编译运行pyqt的方法
1.代码直接进行编译
2.通过UI方式界面设计转换生成对应的py文件,再编写主程序函数对其进行调用
1.代码直接进行编译
import sys
from PyQt5.QtWidgets import (QWidget, QToolTip,QPushButton, QApplication)
from PyQt5.QtGui import QFont
if __name__ == '__main__':
app = QApplication(sys.argv)
btn = QPushButton('Hello world')
btn.resize(100,30)
btn.show()
sys.exit(app.exec_())
通过python *.py的方式即可进行编译
2.通过UI方式界面设计转换生成对应的py文件,再编写主程序函数对其进行调用
通过QT Creator的方式设计界面,主要在.ui文件中进行绘制。
通过pyuic5命令可以对.ui文件进行转换。例:
pyuic5 mainwindow.ui -o mainwindow.py
##mainwindow.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(400, 335)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.pushButton = QtWidgets.QPushButton(self.centralWidget)
self.pushButton.setGeometry(QtCore.QRect(140, 110, 84, 28))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralWidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "hello world"))
编写主函数程序main.py,与mainwindow.py同时进行编译,即可运行出结果。
##main.py
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
import Ui_mainus
if __name__ == '__main__':
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = Ui_mainus.Ui_Dialog()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
三、Qt 与Qtopia Core:
3.1 Qt入门
3.1.1 Hello World
程序的编译方式上述章节中已经介绍过了,可以参考上述方式进行编译。我们从t1.py程序——hello world开始介绍pyqt。
##t1.py
import sys
from PyQt5.QtWidgets import (QWidget, QToolTip,QPushButton, QApplication)
from PyQt5.QtGui import QFont
if __name__ == '__main__':
app = QApplication(sys.argv)
btn = QPushButton('Hello world')
btn.resize(100,30)
btn.show()
sys.exit(app.exec_())
pyqt程序的运行,首先需要导入各类相关的库,特别是QApplication类,每个QT GUI的应用程序都必须有一个且仅有一个QApplication对象。QApplication管理各类资源,例如字体和光标等。QPushButton也是一个重要的类,是GUI中按钮的类。
- 5行为主函数定义。
- 6行构造QApplication实例对象app。
- 7行为QPushButton实例,构造时可传入文字、图片等参量,本例中’Hello world’是构造QPushButton实例btn时传入的参量。
- 8行设置QPushButton的宽度为100像素,高度为30像素。
- 9行显示btn实例,由于无QWidget等主界面的构造,所以QPushButton即为主界面。
3.1.2 结束退出
本例中主要介绍字体变换以及程序退出结束链接。
##t2.py
import sys
from PyQt5.QtWidgets import (QWidget, QToolTip,QPushButton, QApplication)
from PyQt5.QtGui import QFont
from PyQt5.QtCore import QCoreApplication
if __name__ == '__main__':
app = QApplication(sys.argv)
quit = QPushButton('quit')
quit.resize(100,30)
quit.setFont(QFont("Times", 18));
quit.clicked.connect(QCoreApplication.instance().quit)
quit.show()
sys.exit(app.exec_())
- 本例中导入的库中主要包括QFont类,主要用来进行字体变换;导入QCoreApplication类,主要用来保证按钮的退出链接功能。
- 10行通过setFont的方式对QPushButton进行字体设置,本例中设置字体为times,大小为18点。
- 11行中引入了QT中非常重要的connect机制,调用connect可以为两个对象建立联系,本例中就是将QPushButton类quit实例中的点击事件clicked(signal),与程序退出quit(solt)建立连接,quit的clicked事件执行则成员函数quit()就会触发。
3.1.3 父子Widget
t3程序是建立父、子Widget的例子
##t3.py
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication
from PyQt5.QtCore import QCoreApplication
class Example(QWidget):
def __init__(self):
super(Example,self).__init__()
self.initUI()
def initUI(self):
qbtn = QPushButton('Quit', self) qbtn.clicked.connect(QCoreApplication.instance().quit)
qbtn.resize(qbtn.sizeHint())
qbtn.move(50, 50)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Quit button')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
- 首先本例中需要导入QWidget类
- 4行构造一个QWidget对象Example。QWidget是所有接口的基类,并绘制QWidget的显示内容。
- 6行为该类的初始构造内容,其中需要注意为super(Example,self).init() 函数的使用,pyqt5中需要如此填写参数才是正确的,而其他版本的pyqt可能会有super().init()这样使用情形出现。
- 7行initUI为Example对象的UI构造函数
- 9行为在Example对象中构造QPushButton类qbtn,并对其位置进行定义
- 12行对Example的大小位置进行定义,并设置Example的标题
- 最后对父QWidget进行显示,子类的QPushButton类也会同时进行显示
3.1.4 使用QT组件
t4.py程序中主要讲解QT组件QLCDNumber和QSlider两个组件的使用,这些QT组件在父 widge上的位置由 QVBoxLayou类的成员函数设置。在父widge中集成了3个子widge。
##t4.py
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
QVBoxLayout, QApplication)
class Example(QWidget):
def __init__(self):
super(Example,self).__init__()
self.initUI()
def initUI(self):
lcd = QLCDNumber(self)
sld = QSlider(Qt.Horizontal,self)
vbox = QVBoxLayout()
vbox.addWidget(lcd)
vbox.addWidget(sld)
self.setLayout(vbox)
sld.valueChanged.connect(lcd.display)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Signal & slot')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
- 9行中构造QLCDNumber对象
- 10行中构造QSlider对象,并且设置其方向为横向Horizontal(默认为纵向)
- 11行通过QVBoxLayout对QLCDNumber,QSlider进行布局
- 利用connect机制,将QSlider数值变化机制与QLCDNumber数字变化显示进行连接,达到QSlider滑动控制QLCDNumber数字显示变化的效果
3.1.5 封装多个Widget
本例中先将QLCDNumber,QSlider封装为一个类,再将9个类封装在一起。
##t5.py
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
QVBoxLayout, QApplication, QGridLayout)
class LCDRang(QWidget):
def __init__(self, parent = None):
super(LCDRang,self).__init__(parent)
self.lcd = QLCDNumber(self)
self.sld = QSlider(Qt.Horizontal,self)
self.vbox = QVBoxLayout()
self.vbox.addWidget(self.lcd)
self.vbox.addWidget(self.sld)
self.setLayout(self.vbox)
self.sld.valueChanged.connect(self.lcd.display)
class Winform(QWidget):
def __init__(self,parent=None):
super(Winform,self).__init__(parent)
self.initUI()
def initUI(self):
grid = QGridLayout()
self.setLayout(grid)
for row in range(0,3):
for column in range(0,3):
self.lcdrang = LCDRang(self)
grid.addWidget(self.lcdrang, row, column)
self.move(300, 150)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Winform()
ex.show()
sys.exit(app.exec_())
- 本例重点在于通过LCDRang类对QLCDNumber, QSlider两个类进行封装,再在Winform类中对9个类进行合理的放置
3.2 QT的主要特征
3.2.1 signal/slot机制
在GUI系统中,当某个wdgt改变状态(如形态、显示内容),其它 widget常常要做相应改变,各对象之间的消息传递和信息传送是GU程序设计的重要组成部分。早期GUI系统的 toolkit通常使用回调函数机制实现对象之间的联系。QT利用 signal/slot机制实现对象之间的通讯,这是Qt最突出的特征。
下面通过t6.py介绍几种信号的使用方式、与槽的连接方式
##t6.py
from PyQt5.QtWidgets import (QApplication,QWidget,QPushButton,QTextBrowser,QGridLayout)
from PyQt5.QtCore import Qt,pyqtSignal
import sys
class Example(QWidget):
# 声明无参数的信号
signal1 = pyqtSignal()
# 声明带一个int类型参数的信号
signal2 = pyqtSignal(int)
# 声明带int和str类型参数的信号
signal3 = pyqtSignal(int, str)
# 声明带一个列表类型参数的信号
signal4 = pyqtSignal(list)
# 声明带一个字典类型参数的信号
signal5 = pyqtSignal(dict)
# 声明一个多重载版本的信号,包括带int和str类型参数的信号和带str类型参数的信号
signal6 = pyqtSignal([int, str], [str])
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(300,300,450,380)
self.setWindowTitle("自定义信号与槽练习")
gridLayout = QGridLayout()
self.btn1 = QPushButton("无参数信号")
self.btn2 = QPushButton("int信号")
self.btn3 = QPushButton("int和str信号")
self.btn4 = QPushButton("list信号")
self.btn5 = QPushButton("dict信号")
self.btn6 = QPushButton("多重载信号[int,str],[str]")
self.textBrowser = QTextBrowser()
gridLayout.addWidget(self.textBrowser,0,0,4,12)
gridLayout.addWidget(self.btn1,5,0,1,2)
gridLayout.addWidget(self.btn2,5,2,1,2)
gridLayout.addWidget(self.btn3,5,4,1,2)
gridLayout.addWidget(self.btn4,5,6,1,2)
gridLayout.addWidget(self.btn5,5,8,1,2)
gridLayout.addWidget(self.btn6,5,10,1,2)
self.setLayout(gridLayout)
self.mytxt = ""
#空信号
self.btn1.clicked.connect(self.mySignal1)
self.signal1.connect(self.mySlotFunc1)
#int数字信号
self.btn2.clicked.connect(self.mySignal2)
self.signal2.connect(self.mySlotFunc2)
#int和str(数字和字符串)信号
self.btn3.clicked.connect(self.mySignal3)
self.signal3.connect(self.mySlotFunc3)
#list列表信号
self.btn4.clicked.connect(self.mySignal4)
self.signal4.connect(self.mySlotFunc4)
#dict字典信号
self.btn5.clicked.connect(self.mySignal5)
self.signal5.connect(self.mySlotFunc5)
# 多重载信号
def mySignal1(self):
self.signal1.emit()
def mySlotFunc1(self):
self.mytxt += "无参数的信号"
self.textBrowser.setText(self.mytxt)
def mySignal2(self):
self.signal2.emit(123456)
def mySlotFunc2(self,val):
self.textBrowser.setText(str(val))
def mySignal3(self):
self.signal3.emit(123456,"这是我的电话号码:")
def mySlotFunc3(self, val,text):
self.textBrowser.setText(text+str(val))
def mySignal4(self):
self.signal4.emit([1,5,9,0,0,0,0,1,2,3,4])
def mySlotFunc4(self,li):
print(li)
for i in li:
self.mytxt += str(i)
# print(self.mytxt)
self.textBrowser.setText(self.mytxt)
def mySignal5(self):
self.signal5.emit({"phone":90001111,
"addr":["浙江","宁波"]})
def mySlotFunc5(self,mydict):
self.textBrowser.setText(str(mydict["phone"])+str(mydict["addr"][0])+str(mydict["addr"][1]))
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
四、Qt 与Qtopia Core 实验
4.1数字时钟
##test1.py
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QWidget,QApplication,QLCDNumber,QVBoxLayout,QMessageBox,QPushButton
import sys
import time
class MyTime(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.init_timer()
#UI界面搭建
def update_time(self):
self.lcd.display(time.strftime('%X', time.localtime()))
def init_timer(self):
self.timer=QTimer()
self.timer.setInterval(1000)#设置定时器 1S触发一次
self.timer.start()#启动定时器
self.timer.timeout.connect(self.update_time)
def initUI(self):
self.resize(400,200)
self.setWindowTitle("创意时钟")###名称
self.setWindowIcon(QIcon('xiaomayun.jpg'))#图标
#初始化 调色板
self.pl=QPalette()
self.pl.setColor(QPalette.Background,Qt.darkYellow)
self.setAutoFillBackground(True)
self.setPalette(self.pl)#设置顶层布局
self.lcd=QLCDNumber() #初始化lcd
self.lcd.setDigitCount(10)#设置数字个数
self.lcd.setMode(QLCDNumber.Dec)#数字十进制
self.lcd.setSegmentStyle(QLCDNumber.Flat)#平面模式
self.lcd.display(time.strftime('%X',time.localtime()))
##初始化盒子布局
self.box_layout=QVBoxLayout()
self.box_layout.addWidget(self.lcd)#添加LCD组件
self.box_layout.setAlignment(Qt.AlignCenter)#设置组件在布局中间
self.setLayout(self.box_layout)#设置窗体布局
self.btn = QPushButton('Button', self)##创建按钮 测试用
self.btn.setToolTip('This is a <b>QPushButton</b> widget')
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
self.btn.clicked.connect(self.on_click)
self.box_layout.addWidget(self.btn)
# btn.move(50, 50)
self.qbtn = QPushButton('Quit', self)
self.qbtn.clicked.connect(QCoreApplication.instance().quit)
self.qbtn.resize(self.qbtn.sizeHint())
self.qbtn.move(300, 150)
self.show()
"""创建鼠标点击事件"""
def on_click(self):
print("PyQt5 button click")
def closeEvent(self, event):
reply = QMessageBox.question(self, 'Message',
"Are you sure to quit?", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
# 第一个字符串的内容被显示在标题栏上。第二个字符串是对话框上显示的文本。第三个参数指定了显示在对话框上的按钮集合。最后一个参数是默认选中的按钮。
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == '__main__':
app=QApplication(sys.argv)
mt=MyTime()
app.exec_()
五、SQLite
SQLite是一个轻量级的数据库,实现了自给自足、无服务器、零配置、事务性的SQL数据库引擎,主要作为手机应用的数据库以及小型桌面应用的数据库。
5.1 数据库连接实例
##connect.py
import sys
from PyQt5.QtSql import QSqlDatabase
from PyQt5.QtCore import *
if __name__ == "__main__":
app = QCoreApplication(sys.argv)
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("/home/user/test.db")
if db.open():
print("open DB success.")
sys.exit(app.exec_())
5.2 执行SQL语句
QSqlQuery具有执行和操作SQL语句的功能,可以执行DDL和DML类型的SQL查询,QSqlQuery.exec_()用于执行SQL操作。
##exec.py
import sys
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
from PyQt5.QtCore import *
def createDB():
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("/home/user/test.db")
if db.open():
query = QSqlQuery()
query.exec_("create table person(id int primary key, name varchar(20), address varchar(30))")
query.exec_("insert into person values(1, 'Bauer', 'beijing')")
query.exec_("insert into person values(2, 'Jack', 'shanghai')")
query.exec_("insert into person values(3, 'Alex', 'chengdu')")
db.close()
if __name__ == "__main__":
app = QCoreApplication(sys.argv)
createDB()
sys.exit(app.exec_())
5.3 分页查询实现
读取sqlite中的表,并初始化表格数据模型。
import sys
from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel, QSqlQueryModel
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import re
class DataGrid(QWidget):
def __init__(self, parent=None):
super(DataGrid, self).__init__(parent)
# 声明数据库连接
self.db = None
# 布局管理器
self.layout = QVBoxLayout()
# 查询模型
self.queryModel = QSqlQueryModel()
# 表格视图
self.tableView = QTableView()
self.tableView.setModel(self.queryModel)
#
self.totalPageLabel = QLabel()
self.currentPageLabel = QLabel()
self.switchPageLineEdit = QLineEdit()
self.prevButton = QPushButton("Prev")
self.nextButton = QPushButton("Next")
self.switchPageButton = QPushButton("Switch")
# 当前页
self.currentPage = 1
# 总页数
self.totalPage = None
# 总记录数
self.totalRecordCount = None
# 每页记录数
self.pageRecordCount = 4
self.initUI()
self.initializedModel()
self.setUpConnect()
self.updateStatus()
def initUI(self):
self.tableView.horizontalHeader().setStretchLastSection(True)
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.layout.addWidget(self.tableView)
hLayout = QHBoxLayout()
hLayout.addWidget(self.prevButton)
hLayout.addWidget(self.nextButton)
hLayout.addWidget(QLabel("跳转到"))
self.switchPageLineEdit.setFixedWidth(40)
hLayout.addWidget(self.switchPageLineEdit)
hLayout.addWidget(QLabel("页"))
hLayout.addWidget(self.switchPageButton)
hLayout.addWidget(QLabel("当前页:"))
hLayout.addWidget(self.currentPageLabel)
hLayout.addWidget(QLabel("总页数:"))
hLayout.addWidget(self.totalPageLabel)
hLayout.addStretch(1)
self.layout.addLayout(hLayout)
self.setLayout(self.layout)
self.setWindowTitle("DataGrid")
self.resize(600, 300)
def setUpConnect(self):
self.prevButton.clicked.connect(self.onPrevPage)
self.nextButton.clicked.connect(self.onNextPage)
self.switchPageButton.clicked.connect(self.onSwitchPage)
def initializedModel(self):
self.db = QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName("/home/user/test.db")
if not self.db.open():
return False
self.queryModel.setHeaderData(0, Qt.Horizontal, "ID")
self.queryModel.setHeaderData(1, Qt.Horizontal, "Name")
self.queryModel.setHeaderData(2, Qt.Horizontal, "Sex")
self.queryModel.setHeaderData(3, Qt.Horizontal, "Age")
# 获取表的所有记录数
sql = "SELECT * FROM student"
self.queryModel.setQuery(sql, self.db)
self.totalRecordCount = self.queryModel.rowCount()
if self.totalRecordCount % self.pageRecordCount == 0:
self.totalPage = self.totalRecordCount / self.pageRecordCount
else:
self.totalPage = int(self.totalRecordCount / self.pageRecordCount) + 1
# 显示第1页
sql = "SELECT * FROM student limit %d,%d" % (0, self.pageRecordCount)
self.queryModel.setQuery(sql, self.db)
def onPrevPage(self):
self.currentPage -= 1
limitIndex = (self.currentPage - 1) * self.pageRecordCount
self.queryRecord(limitIndex)
self.updateStatus()
def onNextPage(self):
self.currentPage += 1
limitIndex = (self.currentPage - 1) * self.pageRecordCount
self.queryRecord(limitIndex)
self.updateStatus()
def onSwitchPage(self):
szText = self.switchPageLineEdit.text()
pattern = re.compile('^[0-9]+$')
match = pattern.match(szText)
if not match:
QMessageBox.information(self, "提示", "请输入数字.")
return
if szText == "":
QMessageBox.information(self, "提示", "请输入跳转页面.")
return
pageIndex = int(szText)
if pageIndex > self.totalPage or pageIndex < 1:
QMessageBox.information(self, "提示", "没有指定的页,清重新输入.")
return
limitIndex = (pageIndex - 1) * self.pageRecordCount
self.queryRecord(limitIndex)
self.currentPage = pageIndex
self.updateStatus()
# 根据分页查询记录
def queryRecord(self, limitIndex):
sql = "SELECT * FROM student limit %d,%d" % (limitIndex, self.pageRecordCount)
self.queryModel.setQuery(sql)
# 更新空间状态
def updateStatus(self):
self.currentPageLabel.setText(str(self.currentPage))
self.totalPageLabel.setText(str(self.totalPage))
if self.currentPage <= 1:
self.prevButton.setEnabled(False)
else:
self.prevButton.setEnabled(True)
if self.currentPage >= self.totalPage:
self.nextButton.setEnabled(False)
else:
self.nextButton.setEnabled(True)
# 界面关闭时关闭数据库连接
def closeEvent(self, event):
self.db.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = DataGrid()
window.show()
sys.exit(app.exec_())