pyqt5是pyhon的一个gui界面程序开发,对于测试而言,了解和掌握它,可以有利于理解前后端项目的开发,也可以利用它做一个小工具,qt的designer界面设计工具快速的设计出想要的界面,把重心放到逻辑开发上。对于没有开发经验的测试来说,非常适合,掌握后可以将以前学的零散的python知识运用起来写程序。
废话不多说了,下面我开始介绍pyqt5的
一 、 Pyqt5 环境搭建
1 . 安装pytqt 5 库,和pyside 2 库
pip install pyqt5 -i pypi.douban.com/simple
pip install PySide2 -i pypi.douban.com/simple/ --trusted-host pypi.douban.com
2 . pycharm关联qtdesigner工具, 和pyqt5 uic转化工具
qtdesigner路径选择site-packages中的pyside2中的designer
Pyqt5uic路径选择script中的pyuic5.exe
$FileName$ -o $FileNameWithoutExtension$_UI.py 为了规范,加了一个_ui ,表示它是ui文件转化的
$FileDir$
使用时表示在当前路径下,执行了 pyuic5 -o xx_ui.py xx.ui
3. Pycharm中打开designer和ui文件转化
二、基本代码结构
目标:界面代码和逻辑分离。
1. 使用desinger界面设计,并保存ui文件
注:新建时选择 Widget
2. Ui文件转py文件,得到如下
补充如下代码,可以运行界面程序(如果是Wiget)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
Window = QtWidgets.QWidget() # 创建一个窗口
ui = Ui_Form() # 获取ui对象
ui.setupUi(Window) # 表示ui界面,加载到此窗口上
Window.show() # 展示界面
sys.exit(app.exec_())
如果是mainwidow
if __name__ == '__main__':
import sys
app =QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui =Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
3. 编写该界面的逻辑代码
新建一个py文件
如下:实现,【点击按钮,就打印输入框中的内容】,前提,ui文件新建的是Widget
```js
from PyQt5.Qt import *
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from test_case.test import Ui_Form
class Login(QWidget,Ui_Form):
def __init__(self,parent=None,*args,**kwargs):
super().__init__(parent,*args,**kwargs)
self.setupUi(self)
self.func_list()
def func_list(self):
self.func()
def func(self):
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Login()
window.show()
sys.exit(app.exec_())
如果ui文件是新建的main window
类要继承QtWidgets.QMainWindow, 代码如下, 下面的写法也兼容上面的Widget的ui文件
from PyQt5.Qt import *
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from sq_autotest_tool_ui import Ui_MainWindow
class Login(QtWidgets.QMainWindow,Ui_MainWindow):
def __init__(self,parent=None,*args,**kwargs):
super().__init__(parent,*args,**kwargs)
self.setupUi(self)
self.func_list()
def func_list(self):
self.func()
def func(self):
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Login()
window.show()
sys.exit(app.exec_())
小技巧,可以给以上代码设置一个实时代码:
4. 编写主程序代码
一个程序不光一个界面,主程序代码中就控制该展示哪个界面
三、QObject 类
1 . 所有东西都继承O bject 对象类****
每一个东西都有一个唯一的objectName, 在desiner中可以发现。
也可以用 实例.inherits("A类") # 实例是否继承于A类
按钮、标签、窗口等都要设置objectName
如果是designer设置的ui文件,转化成py文件后, self。ObjectName就代表 那个控件,如下:
相关代码:
对象. objectName() # 货物 objectname
对象.setProperty('level1', '第一')
对象.setProperty('level2', '第二')
对象.property('level1') # 获取property设置的某个属性的值
对象.dynamicPropertyNames() # 获取所有 setProperty设置的属性的对象
这里涉及 QSS样式中的 id选择器, 和 属性选择器。
2. 所有东西(控件等)公用方法-删除
代码中,如果想删除某个控件,用 对象.deleteLater(), 控件被删除时,会触发被摧毁信号, 如:对象.destroyed.connect(lambda: print('对象被释放'))
3. 所有东西(控件等)公用方法-定时器
使用 time_id=对象.startTimer(1000) 设置定时间隔1秒 的 定时器对象 在类下面重写事件 timerEvent() ,可以设置定时做的事情。 停止计时器:对象.killTimer(time_id) # 参数为 定时器对象 基本示例
from PyQt5.Qt import *
import sys
class Obj2(QObject):
def timerEvent(self, QTimerEvent): # 重写事件 (专门的参数用来接东西的)
print(QTimerEvent)
print("我是另一个Qbject继承类的定时器事件")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = QWidget()
obj2=Obj2()
obj2.startTimer(2000)
# obj2.killTimer(timer_id) # 终止定时器(如:可以在某次判断后给它终止)
window.show()
sys.exit(app.exec_())
如果,要给标签或者按钮设置定时器,就自己写个子控件类,标签或者按钮
标签设置定时器案例
由于,timerEvent在子标签类中,重写了,只要是 字标签类的对象 都能使用这个定时器
from PyQt5.Qt import *
import sys
class Window(QWidget):
def __init__(self):
super().__init__() # 调用父类QWidget中的init方法
self.setWindowTitle("软件名称")
self.resize(600, 500)
class Label(QLabel):
def __init__(self, *args, **kwargs): # 这样能接收各种参数
super().__init__(*args, **kwargs) # 保留父类的方法
self.resize(80, 40)
self.move(100, 100)
# self.setText('10')
self.setStyleSheet("background-color:#02CDB9;font-size:25px")
# self.timer_id=self.startTimer(1000)
# 定义初始时间
def setSec(self,sec):
self.setText(str(sec))
# 定义到计时时间间隔
def ms(self,ms):
self.timer_id = self.startTimer(ms)
def timerEvent(self, *args, **kwargs):
print('事件案例演示')
# 获取text中的数据
sec = int(self.text())
# 倒计时
sec -= 1
self.setText(str(sec))
# 停止计时
if sec == 0:
self.killTimer( self.timer_id) #timer_id 是全局的变量,所以这里可以用
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
label = Label(window)
label.setSec(20) # 设置初始时间
label.ms(1000) # 设置跳转时间
# timer_id = label.startTimer(1000)
# # label.killTimer(timer_id)
window.show()
sys.exit(app.exec_())
下面是一个无用的例子,可以实现按钮定时的去点击
四、事件、信号和槽(动作)
要完成对程序的操作,需要用鼠标和键盘对程序某些事件,
触发事件后,就会释放默认的一些信号,或者也可以自定义信号,
通过信号,去触发接下来的动作,也就是pyqt5的核心,信号和槽。
1.事件
举例:按钮类QPushButton 的一些事件。
有点击,双击,按下,释放等。
查看事件代码中点击QPushButton 查看事件
qtdesiner 也可以查看信号猜测有哪些事件
查看下面效果
1.可以发现程序会先分发到event方法,再往下分发
- 可以去修改按下事件和双击事件
from PyQt5.Qt import *
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('事件机制')
self.resize(600, 450)
self.move(300, 300)
class Btn(QPushButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.move(60, 60)
self.resize(50, 35)
self.setText('按钮控件')
self.setStyleSheet('background-color:green')
# # 重写event方法
def event(self, evt): # 之前的功能是会分发 到不同的信号
# print(evt,"任何一个信号都要经过event方法", 'WWWWWWWWWW')
return super().event(evt) #有这句才可以向下分发(鼠标按下事件)
def mousePressEvent(self, evt):
# print("鼠标按下事件")
return super().mousePressEvent(evt) # 把父类的 同一个方法向下 分发
def mouseDoubleClickEvent(self,evt):
print("双击")
return super().mouseDoubleClickEvent(evt)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
btn = Btn(window)
window.show()
sys.exit(app.exec_())
窗口QWidget当然,也有一些事件,如,鼠标右击事件,简单看看效果如下:
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('事件机制')
self.resize(600, 450)
self.move(300, 300)
def contextMenuEvent(self, evt): # 重写方法,名字要写对
print("在窗口中点击右键")
return super().contextMenuEvent(evt)
利用这点,再结合QMenu可以实现右击后出现菜单的功能。 前面Qobject提到的定时器,也是一个事件。
2.信号,以及传递参数
信号在释放时,可以不带参数,也可以带参数。比如:
a. 单选按钮进行点击事件时,可以传递点击时是否选中的Bool值
b. 文本输入框编辑完关闭离开事件时,会传递 编辑文本(str格式)
c. 多选框,切换选项事件时,当前文本改变信号时,会传递所选的文本(str)
d. 数字调节器, 调节数值时,值改变信号,传递值(传的可以是整形,也可以是文本) 在qtdesiner 中,可以去查看不同控件的信号,以及信号要传递的参数
如 数值步长调节器,文本改变信号时,可以传递整形,也可以传递字符串,就可以在信号后面加中括号【】。如下写法:
self.qsb.valueChanged[int].connect(lambda val: print(type(val)) # 信号传出来的时 int类型\
self.qsb.valueChanged[str].connect(lambda val: print(type(val))) # 信号传出来的时 str类型
自定义信号
通过改变事件方法,在事件中用pyqtSignal().emit()发出信号。
如下例子,在按钮子类中, 改变按钮按钮事件 (变成鼠标右键按下)
class Btn(QPushButton):
# 右键信号
rightSignal = pyqtSignal() # 先定义好一个信号,然后寻找这个信号所属事件
def mousePressEvent(self, evt):
super().mousePressEvent(evt)
# print(evt.button()) # 可以看出,左键点击是1 ,右键点击是2
# print(evt) #<PyQt5.QtGui.QMouseEvent object at 0x0000022ED32583A8> 可以看出他是 QMouseEvent 里面的
#evt.button() 为2 表示右键
# if evt.button() == 2:
# Qt.RightButton 也表示2
if evt.button() == Qt.RightButton: # 点进去看,也可以知道是2
# print('鼠标右键被按下') # 这是用来测试右键是否生效,不是槽函数功能
self.rightSignal.emit() # 通过emit函数来把之定义的信号发射出去
以上例子,不用多在意,接着看信号传递参数, 理解就好,方便对控件的信心进行理解
a. 定义信号 (表示,信号可以释放的数据类型)
i. rightSignal = pyqtSignal([str],[str,int])
b. 释放信号 (要传递哪种数据类型的参数,就在信号对象后写 【】)如下
i. self.rightSignal[str].emit("一个参数")
ii. self.rightSignal[str,int].emit(“一个参数”,555)
c. 信号连接槽函数 (使用时,同样是写哪个类型,就能释放哪个信号),如下
i. btn.rightSignal[str,int].connect(槽函数)
ii. btn.rightSignal[str].connect(槽函数)
装饰器实现信号和槽连接,对于其他信号能否也生效,未做研究 :如下简单的例子:
3.槽函数
如果信号传递出了参数,定义槽函数时,对应的槽函数,需要能接收到,
即:定义的槽函数需要传递参数,如下
def get_msg(a,b)
pass
def get_msg2(a)
pass
对象.sendmsged[str,int].connect(get_msg)
对象.sendmsged[str].connect(get_msg2)
四、QWidget
常用事件
1.窗口事件
- QWidget有窗口事件,可以在定义QWidget子类时,重新这些事件,查看效果
窗口打开事件showEvent、
窗口关闭closeEvent事件、
窗口缩放resizeEvent事件,
2.鼠标事件
2. QWidget的一些鼠标的事件,例子如下
鼠标按下mousePressEvent,鼠标松开mouseReleaseEvent,
鼠标双击 mouseDoubleClickEvent 鼠标移动mouseMoveEvent
鼠标进入enterEvent,
鼠标离开 leaveEvent
鼠标右击 contextMenuEvent
from PyQt5.Qt import *
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("获取和设置控件尺寸、大小以及控件大小尺寸限定")
self.resize(600,500)
self.func_list()
def enterEvent(self, QEvent):
print('鼠标进入事件')
self.setStyleSheet("background-color:red;")
def leaveEvent(self, QEvent):
print('鼠标离开事件')
self.setStyleSheet("background-color:green;")
def contextMenuEvent(self, evt):
super().contextMenuEvent(evt)
# 重写方法,名字要写对
print("在窗口中点击右键")
print(evt)
def func_list(self):
self.func()
def func(self):
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
"""针对mouseMoveEvent的设置成---- 不按下鼠标。也能鼠标跟踪移动"""
# window.setMouseTracking(True)
window.show()
sys.exit(app.exec_())
鼠标按下事件,可以从evtent中获取 点击的位置
3.键盘事件
在Qwidget子类中 ,需重写事件,注意的是, 拼接 虚键用 | , 拼接实键用 and
# 键盘按下
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_5: #
print(' 按5键')
if QKeyEvent.modifiers() == Qt.ControlModifier and QKeyEvent.key() == Qt.Key_C:
print('按 ctrl+c')
# ctrl+ shift+ c (两个虚键用 或者 | 拼接 实键用 and )
if QKeyEvent.modifiers() == Qt.ControlModifier | Qt.ShiftModifier and QKeyEvent.key() == Qt.Key_C:
print('ctrl+ shift+ c')
print("随便按")
# 键盘松开
def keyReleaseEvent(self, QKeyEvent):
print('释放')
常用方法
对象.setMinimumSize(200, 200) # 允许最小尺寸
对象.setMaximumSize(500, 600) # 允许最大尺寸
对象.setGeometry(50, 200, 60, 30) # 设置位置和大小
对象..setContentsMargins(50, 50, 10, 0) # 设置控件边界(如下图)
print(label.contentsRect()) # 获取内容尺寸对象 (相对位置,大小)
label2.lower() # 置于底层 见图(层级关系)
label1.raise_() # 置于最上层
label2.stackUnder(label1) # 对象2置于 对象1之下
对象.childAt(2, 2) #查看 window 的 (2,2)处的 子控件
对象.parentWidget() # 查看他的父控件
对象.childrenRect() # 获取所有子控件,相对于本身的边界
常用方法系列2
# btn.setEnabled(False) # 是否可用
# btn.setVisible(False) # 是否显示
# btn.setHidden(False) # 是否隐藏
# window.show() # 展示, 父window展示,子控件按钮也展示了
# window.hide() # 隐藏主窗口
btn.hide() # 隐藏按钮
"""允许在title中写字符串 [*] 展示出来是 * """
window.setWindowModified(True) # 表示为编辑状态没啥用
"""是否活跃"""
window1.show()
window.show() # 代码在后面的,层级在上面, 为活跃状态
print(window.isActiveWindow()) # 层级在上,为活跃状态
print(window1.isActiveWindow())
self.setWindowFlags(Qt.FramelessWindowHint) #无边框
鼠标样式设置
自定义图标:使用QPixmap和QCursor
pixmap= QPixmap('aaa.png')
new_pixmap = pixmap.scaled(20,20) # 设置鼠标样式图片的大小
cursor = QCursor(new_pixmap,1,1) # 表示鼠标变样式时的一个 过度范围
window.setCursor(cursor)
设置最大化最小化按钮
假如要设置无边框的东西,即没有上面的部分,它就不能拖动,也不饿能最大化,最小化了,这个时候自己来定义定义
self.setWindowFlags(Qt.FramelessWindowHint) # 设置成无边框
要使用的代码如下
按钮相关
close() 关闭
showMinimized() 最小化
self.showMaximized() 最大化
self.isMaximized() 是否最大化
self.showMaximized() 是否最小化
self.showNormal() # 恢复
拖动相关
要修改按下事件,修改移动事件,修改松开事件
逻辑:鼠标按钮下记录 相对坐标和全局坐标
鼠标移动,找到移动后的全局坐标,计算移动量,让相对坐标移动
释放事件, 让移动都在一个标记下进行,鼠标释放,让它
再设置个透明度
from PyQt5.Qt import *
import sys
class Window(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowFlags(Qt.FramelessWindowHint) # 无边框
self.setWindowOpacity(0.7) # 半透明,必须是浮点型
self.setWindowTitle('窗口案例')
self.resize(600, 450)
self.mouse_press = False # 初始的标记,鼠标按下之后改变标记
# self.move(300, 300)
self.btn_w = 50
self.btn_x = 20
self.func_list()
def func_list(self):
self.btn()
def btn(self):
# 添加最大化、最小化和关闭按钮
self.close_btn = QPushButton(self)
self.close_btn.setText('关闭')
self.close_btn.resize(self.btn_w, self.btn_x)
self.max_btn = QPushButton(self)
self.max_btn.setText('最大化')
self.max_btn.resize(self.btn_w, self.btn_x)
self.min_btn = QPushButton(self)
self.min_btn.setText('最小化')
self.min_btn.resize(self.btn_w, self.btn_x)
self.close_btn.pressed.connect(self.close) # 关闭按钮 的 按下信号 触发 关闭槽函数
def max_signal():
if self.isMaximized(): # 如果 是最大化时:
self.showNormal()
self.max_btn.setText('最大化')
else: # 如果是正常时:
self.showMaximized()
self.max_btn.setText('恢复')
self.max_btn.pressed.connect(max_signal)
self.min_btn.pressed.connect(self.showMinimized) # 最小化按钮 的 按下信号 触发 展示最小化
"""窗口大小变化,也可以跟着自由变化"""
def resizeEvent(self, QResizeEvent):
self.close_btn.move(self.width() - 50, 2)
self.max_btn.move(self.width() - 100, 2)
self.min_btn.move(self.width() - 150, 2)
"""鼠标按下记录起始位置"""
def mousePressEvent(self, QMouseEvent):
if QMouseEvent.button() == Qt.LeftButton: # 左键才能移动
self.mouse_press = True # 需要在属性中先定义为False
self.win_x = self.x() # 鼠标按扭下的 坐标(窗口相对位置)
self.win_y = self.y()
self.m_x = QMouseEvent.globalX() # 鼠标按扭下的 坐标(屏幕全局)
self.m_y = QMouseEvent.globalY()
# 1.创建一个标记,用来判定鼠标只有在按下之后才能移动
# 2.窗口的原始坐标
# 3.鼠标按下的坐标
"""鼠标移动根据全局坐标移动量,来让按下的起始位置移动"""
def mouseMoveEvent(self, QMouseEvent):
if self.mouse_press:
move_x = QMouseEvent.globalX() # 鼠标移动后的 全局坐标(屏幕全局)
move_y = QMouseEvent.globalY()
# 移动量
xx = move_x - self.m_x
yy = move_y - self.m_y
self.move(self.win_x + xx, self.win_y + yy)
# if 窗口标记 == True:
# 2.根据鼠标按下的点计算移动量
# 3.根据移动量和窗口的原始坐标得到新坐标
# 4.移动窗口位置
""" 松开后,把标记改为False ,这样就保证了按下的时候才会移动"""
def mouseReleaseEvent(self, QMouseEvent):
self.mouse_press = False
# 1.把mousePressEvent中创建的标记重置为False
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
焦点控制
默认可以用鼠标和tab切换 焦点
# TabFocus 只能使用Tab键才能获取焦点
# ClickFocus 只能使用鼠标点击才能获取焦点
# StrongFocus 上面两种都行
# NoFocus 上面两种都不行
led2.setFocusPolicy(Qt.ClickFocus)
led2.setFocus() # 设置一个焦点
对于qt5的各种控件总结,我会用界面工具设计出一个学习和查看的工具出来,已经完成了一部分,如下
后续回接着更新,感谢支持!
-----本人是一个测试菜鸟,理解不到位还请包涵。