目录
一、编辑Tab顺序
二、编辑伙伴
三、设置快捷键(仅MainWindow可用)
四、信号槽
【基本介绍】
【常用信号槽】控件对窗体(拖地)
【常用信号槽】控件对控件
【自定义信号槽】步骤
五、设计文件的转化
六、GUI的运行
1.main.py的建立(卡片式布局不适用)
2.生成代码中setupUI的更改
3.Dto.py的建立(全局变量库)
【Dto.py】(和Event.py并列)
【Event.py】调用dto(ui转化来的)
4.BLL.py的建立
5.总结:运行一个GUI的步骤
七、可移动的卡片式布局demo
1.main.py的建立(卡片式布局)
2.生成代码setupUI的添加
3.预览效果
八、常见问题
Q1:如何给树添加子节点:
Q2:坐标怎么看
Q3:禁止放大怎么设置
一、编辑Tab顺序
点击下面这个按钮后,按控件调整tab的顺序,设置好后,鼠标聚焦在输入框1中,按一下tab鼠标聚焦会跳到下一个输入框中
编辑tab结束后,按下面这个按钮重新返回页面布局
二、编辑伙伴
(删除伙伴的方法:框选-右键选择全部-删除)
三、设置快捷键(仅MainWindow可用)
例如我菜单(MainWindow有菜单栏,Widget没有),有一个作者介绍的动作(action),我可在动作编辑器中加入快捷键
四、信号槽
【基本介绍】
删除槽函数的方法:右键【控件】-点击【选择全部】-【删除】/【全部删除】
编辑槽函数的方法:右下角
【常用信号槽】控件对窗体(拖地)
控件名称 | 触发事件 | 触发的槽函数 | 说明 |
Button | clicked() | close() | 关闭窗体 |
Button | clicked() | showFullScreen() showMaximized() showMinimized() showNormal() | 全屏放大 放大 最小化 窗体大小恢复 |
TreeWidget | clicked(QModelIndex) | 自定义信号槽 | 树组件被点击时触发自定义信号槽 |
各大输入框 | textChanged() | 自定义信号槽 | 输入框输入发生改变时触发自定义信号槽 |
Combobox | currentIndexChanged() | 自定义信号槽 | 选择框选项发生改变时触发自定义信号槽 |
Checkbox/radioBox | toggled(bool) | 自定义信号槽 | 选择选项发生改变时触发自定义信号槽 |
【常用信号槽】控件对控件
主控件 | 从控件 | 触发事件 | 触发结果 | 说明 |
Button | 任意输入框 | clicked() | clear() | 清空输入 |
Button | 任意输入框 | clicked() | 【双箭头】selectall()+copy() | 复制输入框的所有内容 |
Button | 任意输入框 | clicked() | 【双箭头】selectall()+cut() | 剪切输入框的所有内容 |
Button | 任意输入框 | clicked() | paste() | 将粘贴板的内容到输入框 |
Button | 任意输入框 | clicked() | setfocus() | 鼠标聚焦输入框 |
Button | 任意输入框 | clicked() | undo() | 将输入框的输入内容撤销 |
Button | 任意输入框 | clicked() | redo()+undo() | 将输入框的输入内容反撤销 |
Checkbox/radioBox | 任意输入框 | toggled(bool) | setDisabled(bool) | 按一下禁用 再按一下启用 |
Checkbox/radioBox | 任意输入框 | toggled(bool) | setHidden(bool) | 按一下可见 再按一下不可见 |
【备注】不建议用 undo() / redo()+undo(),只要输入框的undoRedoEnabled设置为true支持用户CTRL+Z撤销,用按钮反而麻烦。
【自定义信号槽】步骤
以创建plainTextEdit的textchanged()事件为例,创建一个信号槽(事件触发方法):
生成的代码如下:
self.plainTextEdit.textChanged.connect(Form.executechanged)
需要做两件事情:
①改代码
self.plainTextEdit.textChanged.connect(self.executechanged)
②加信号槽-----事件触发方法(在生成的class Ui_Form(object)类下加即可)
def executechanged(self):
pass#实现你的逻辑
【举个之前的例子】
五、设计文件的转化
添加好pyqt5的外部工具后(如果不会添加,参考这篇文章),转换方法如下:
①找到xxx.ui文件,右键External Tools,选择PyUIC,自动生成一个xxx.py文件
②找到yyy.qrc文件,右键External Tools,选择Pyrcc,自动生成一个yyy.py文件
六、GUI的运行
1.main.py的建立(卡片式布局不适用)
新建一个【main.py】粘贴以下代码进去,这个是启动文件
import untitled
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
if __name__ == '__main__':
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = untitled.Ui_Form() #①untitled是你ui转换后的py文件 ②根据ui设计的窗体类型,看是:Ui_Form 还是 Ui_MainWindow 不知道可以去untitled.py看看类名
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
运行【main.py】,即可运行我们的GUI!
2.生成代码中setupUI的更改
如果有自定义信号槽,connect处 类名=>self 即可(参考上面)。
3.Dto.py的建立(全局变量库)
建立这个类是为了更容易地获取变量和设置变量,我们在获取变量的时候可以给变量设置别名,Dto是一个模板,需要根据实际情况去修改。这步是非必要的。
【Dto.py】(和Event.py并列)
import os
from PyQt5.QtWidgets import QWidget, QApplication, QFileDialog
class Store:
# 初始化变量,你可以【自主命名】,例如将【BookTree】替换掉【treeselect】
# 按照这个模板灵活修改
treeselect = ""
isradio = False
ischeck = False
Comboxtext = ""
Linetext =""
Plaintext =""
# 获取前端的值存进Store类
def UpdateVar(self,Form): #下面的self是传入的Form
""" 调用示例: Dto.Store.UpdateVar(self,Ui_Form) 获取变量前调用"""
try:
#---- 打印控件列表,从控件列表中选择控件赋值 ----
L=[]
for attr_name in dir(self):
attr = getattr(self, attr_name)
if isinstance(attr, QWidget):
L.append(attr_name)
print(L)
#---- 打印控件列表,从控件列表中选择控件赋值 ----
# ------- 变量赋值 -------
Store.treeselect= self.treeWidget.currentItem().text(0) #树选中的节点名称
Store.isradio = True if self.radioButton.isChecked() else False #radiobutton是否被选择
Store.ischeck = True if self.checkBox.isChecked() else False #checkBox是否被选择
Store.Comboxtext = self.comboBox.currentText() #获取Combobox选中的选项文本
Store.Linetext = self.lineEdit.text() #获取lineEdit输入
Store.Plaintext = self.plainTextEdit.toPlainText() #获取plainTextEdit输入
# ------- 变量赋值 -------
# ------- 通过反射打印Store的值 -------
class_attributes = Store.__dict__
for attr, value in class_attributes.items():
if not attr.startswith('__'): # 过滤掉特殊属性
print(f"【反射】变量:{attr},值:{value}")
# ------- 通过反射打印Store的值 -------
except Exception as ex:
print(f"更新变量时发生错误:{ex}")
# 获取前端的输入框控件和值,设置好输入框的值
def Settext(widget, text):
"""调用示例: Dto.Store.Settext(self.plainTextEdit,"我设置的内容")"""
try:
if hasattr(widget, 'setPlainText'):
widget.setPlainText(text)
elif hasattr(widget, 'setText'):
widget.setText(text)
else:
raise TypeError("不支持设置该输入框的值")
except Exception as ex:
print(f"设置输入框的值时发生错误:{ex}")
def EnableChange(widget):
"""调用示例:Dto.Store.EnableChange(self.radioButton)"""
try:
if hasattr(widget, 'isEnabled') and hasattr(widget, 'setEnabled'):
current_state = widget.isEnabled()
widget.setEnabled(not current_state)
else:
raise TypeError("不支持转化")
except Exception as ex:
print(f"控件可用性转化时发生错误:{ex}")
class Common:
def Openfile(self,path):#打开指定路径
"""调用示例:returnstring=Dto.Common.Openfile(self,"F:\Autogenerate")"""
import os
try:
os.startfile(path)
return "已打开路径:"+path
except Exception as ex:
return "出错了:"+str(ex)
def PopWindow(self,num,string):#弹窗提示
"""调用示例:returnstring=Dto.Common.PopWindow(self,2,"您确定吗")"""
#调用示例:
# if(Dto.Common.PopWindow(self,2,"您确定吗")):
# print("yes!")
from PyQt5.QtWidgets import QMessageBox
if (num==1):
return QMessageBox.information(None, "温馨提示", string, QMessageBox.Yes)== QMessageBox.Yes
elif (num==2):
return QMessageBox.question(None, "询问", string, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)== QMessageBox.Yes
elif (num==3):
return QMessageBox.warning(None, "警告", string, QMessageBox.Yes)== QMessageBox.Yes
else:
return QMessageBox.warning(None, "出错", string, QMessageBox.Yes)== QMessageBox.Yes
def WriteFile(self,path,data):#写txt文件
"""调用示例:returnstring=Dto.Common.WriteFile(self, "D:\Autogenerate", "测试测试")"""
import os
from datetime import datetime
try:
os.makedirs(path, exist_ok=True)#不存在则创建路径
timestamp = datetime.now().strftime("%Y-%m-%d %H-%M-%S")# 获取当前时间戳
file_path = os.path.join(path, f"{timestamp}.txt")# 创建文件路径和文件名
with open(file_path, 'w') as file:
file.write(f"""执行时间: {datetime.now().strftime("%Y-%m-%d %H-%M-%S")}\n""")
file.write(data)
return f"""创建成功,时间:{datetime.now().strftime("%Y-%m-%d %H-%M-%S")}"""
except Exception as ex:
return "出错了:"+str(ex)
def Patse(self,data):
"""调用示例:returnstring=Dto.Common.Patse(self, "测试测试")"""
try:
clipboard = QApplication.clipboard()
clipboard.setText(data)
return "复制成功"
except Exception as ex:
return "复制失败:" + str(ex)
def GetFileurl(self):#弹窗让用户选文件,选完返回一个url
"""调用示例:url = Dto.Common.GetFileurl(self)"""
return QFileDialog.getOpenFileName()[0]
def GetFolderurl(self):#弹窗让用户选文件夹,选完返回一个url
"""调用示例:url = Dto.Common.GetFolderurl(self)"""
return QFileDialog.getExistingDirectory()
def GetFolderByFileUrl(self,fileurl):#传入文件路径,传出文件夹路径
"""调用示例:url = Dto.Common.GetFolderByFileUrl(self,"C:\\Users\\ASUS\\Desktop\\temp\\01.pdf")"""
return os.path.dirname(fileurl)
【Event.py】调用dto(ui转化来的)
from PyQt5 import QtCore, QtGui, QtWidgets
import Dto # 1.引入全局变量库,文件头部import Dto
class Ui_Form(object):
def setupUi(self, Form):
#省略自动生成的代码
self.retranslateUi(Form)
self.treeWidget.clicked['QModelIndex'].connect(self.executechanged) # 树被点击时触发自定义槽函数
def retranslateUi(self, Form):
#省略自动生成的代码
def executechanged(self): #自定义槽函数
Dto.Store.UpdateVar(self,Ui_Form) # 2.更新变量值(入参:self Ui_Form类)
print("当前选中的树节点是:")
print(Dto.Store.treeselect)# 3.【使用Dto】获取变量值
Dto.Store.Settext(self.plainTextEdit,"888") # 4.【使用Dto】设置输入框(plainTextEdit Linetext)的值,如果是清空输入框,第二个入参可以是""
Dto.Store.EnableChange(self.radioButton) # 5.【使用Dto】 使控件可用和不可用发生转化
#还要Dto.Common方法自行探索
4.BLL.py的建立
这里一般是写一些逻辑,例如我在里面写一个helloword方法,导入后(import BLL),调用时直接写BLL.helloword()即可调用。
【调用方法注意事项】调用窗体的方法,需要写成“self.MethodName()”,self就是窗体本身。
【说明】这步是非必要的。
5.总结:运行一个GUI的步骤
1.转化好ui文件和qrc文件(转化成.py)
2.完成ui.py中setupUI代码的更改
3.建立main.py(程序的入口,其中下面这句代码,Event是ui文件转py后的名称,Ui_Form是ui生成的类名)
ui = Event.Ui_Form()
4.建立Dto.py(建议,当然你在ui.py下堆代码也行,后期很难维护,下同),建立BLL.py
5.运行main.py
七、可移动的卡片式布局demo
1.main.py的建立(卡片式布局)
【main.py】(卡片式布局,完整)
注意:MainWindow的第2个入参根据实际情况而调整
import Event
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QWidget, Event.Ui_Form):#将这里的【Event.Ui_Form】修改为实际的【py文件名.类名】即可
def __init__(self):
super().__init__()
self.setupUi(self)
self.is_moving = False
self.old_pos = None
def mousePressEvent(self, event):#重写鼠标移动事件
if event.button() == QtCore.Qt.LeftButton:
self.is_moving = True
self.old_pos = event.globalPos()
def mouseMoveEvent(self, event):
if self.is_moving:
delta = event.globalPos() - self.old_pos
self.old_pos = event.globalPos()
self.move(self.pos() + delta)
def mouseReleaseEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.is_moving = False
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
2.生成代码setupUI的添加
【Event.py】增加隐藏标题栏、主窗体背景设为透明的代码
Form.setWindowFlags(QtCore.Qt.FramelessWindowHint) #加上,隐藏标题栏
Form.setAttribute(QtCore.Qt.WA_TranslucentBackground) #加上,窗体透明化
class Ui_Form(object):
def setupUi(self, Form):
#自动生成的代码
Form.setWindowFlags(QtCore.Qt.FramelessWindowHint) #加上,隐藏标题栏
Form.setAttribute(QtCore.Qt.WA_TranslucentBackground) #加上,窗体透明化
def retranslateUi(self, Form):
#自动生成的代码
3.预览效果
如图,以下窗体是可拖动的
八、常见控件的用法
1.通用
self.控件名称.clicked.connect(self.方法名) #点击事件
self.控件名称.textChanged.connect(self.方法名) #值变化事件
self.comboBox.currentIndexChanged.connect(self.方法名) #下拉框值改变事件
2.stackedWideget
print(self.stackedWidget.currentIndex())#打印当前页面索引
print( self.stackedWidget.currentWidget().objectName())#打印当前页面名称
print(self.stackedWidget.count())#打印总页数
self.stackedWidget.setCurrentIndex(2)#跳转到索引=2的页面
九、常见问题
Q1:如何给树添加子节点:
【需要添加两个地方】
注意item_0是父节点,item_1是子节点,一一对应的
Q2:坐标怎么看
含义:起始坐标+宽高
QRect(260, 10, 101, 31)
- 值越大越往右:260
- 值越小越往下:10
- 宽度:101
- 高度:31
Q3:禁止放大怎么设置
Form.setMaximumSize(Form.width(), Form.height())