Pyqt5高级技巧2:Tab顺序、伙伴快捷键、各类常用控件的事件、可移动的卡片式布局(含基础Demo)

目录

一、编辑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.预览效果

八、常见控件的用法

1.通用

2.stackedWideget

九、常见问题

Q1:如何给树和项目列表添加子节点:

Q2:坐标怎么看

Q3:禁止放大怎么设置

Q4:如何设置Icon

Q5:如何默认选中某些元素

Q6:输入框如何显示提示语

Q7:三元运算符在qt开发中经常用,请问是?


一、编辑Tab顺序

        点击下面这个按钮后,按控件调整tab的顺序,设置好后,鼠标聚焦在输入框1中,按一下tab鼠标聚焦会跳到下一个输入框中

        编辑tab结束后,按下面这个按钮重新返回页面布局 

 二、编辑伙伴

 (删除伙伴的方法:框选-右键选择全部-删除)

三、设置快捷键(仅MainWindow可用)

        例如我菜单(MainWindow有菜单栏,Widget没有),有一个作者介绍的动作(action),我可在动作编辑器中加入快捷键

四、信号槽

【基本介绍】

        删除槽函数的方法:右键【控件】-点击【选择全部】-【删除】/【全部删除】

        编辑槽函数的方法:右下角

【常用信号槽】控件对窗体(拖地)

控件名称触发事件触发的槽函数说明
Buttonclicked()close()关闭窗体
Buttonclicked()showFullScreen()
showMaximized()
showMinimized()
showNormal()

全屏放大

放大

最小化

窗体大小恢复

TreeWidgetclicked(QModelIndex)自定义信号槽树组件被点击时触发自定义信号槽
各大输入框textChanged()自定义信号槽输入框输入发生改变时触发自定义信号槽
ComboboxcurrentIndexChanged()自定义信号槽选择框选项发生改变时触发自定义信号槽
Checkbox/radioBoxtoggled(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】
    # 注意每次获取公共变量前需要调用UpdateVar方法,本质是你更新到这,再问这拿
    # 按照这个模板灵活修改
    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输入
            # 项目列表框self.listWidget.selectedItems()[0].text() # 项目列表框选中的节点名称

            # ------- 变量赋值 -------

            # ------- 通过反射打印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:
    @staticmethod
    def Openfile( path):  # 打开指定路径
        """调用示例:returnstring=Dto.Common.Openfile("F:\Autogenerate"),若允许下可用空字符串代替"""
        import os
        try:
            os.startfile(path)
            return "已打开路径:" + path
        except Exception as ex:
            return "出错了:" + str(ex)

    @staticmethod
    def PopWindow( num, string):  # 弹窗提示
        """调用示例:returnstring=Dto.Common.PopWindow(2,"您确定吗"),若允许下可用空字符串代替"""
        # 调用示例:
        # if(Dto.Common.PopWindow(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

    @staticmethod
    def WriteFile( path, data):  # 写txt文件
        """调用示例:returnstring=Dto.Common.WriteFile( "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)

    @staticmethod
    def Patse(data):
        """调用示例:returnstring=Dto.Common.Patse( "测试测试"),若允许下可用空字符串代替"""
        try:
            clipboard = QApplication.clipboard()
            clipboard.setText(data)
            return "复制成功"
        except Exception as ex:
            return "复制失败:" + str(ex)

    @staticmethod
    def GetFileurl():  # 弹窗让用户选文件,选完返回一个url
        """调用示例:url = Dto.Common.GetFileurl(),若允许下可用空字符串代替"""
        return QFileDialog.getOpenFileName()[0]

    @staticmethod
    def GetFolderurl():  # 弹窗让用户选文件夹,选完返回一个url
        """调用示例:url = Dto.Common.GetFolderurl(),若允许下可用空字符串代替"""
        return QFileDialog.getExistingDirectory()

    @staticmethod
    def GetFolderByFileUrl(fileurl):  # 传入文件路径,传出文件夹路径
        """调用示例:url = Dto.Common.GetFolderByFileUrl("C:\\Users\\ASUS\\Desktop\\temp\\01.pdf"),若允许下可用空字符串代替"""
        return os.path.dirname(fileurl)

    @staticmethod
    def GetCurrentPath():#获取当前exe运行的文件路径
        """调用示例:url = Dto.Common.GetCurrentPath(),若允许下可用空字符串代替"""
        return os.getcwd()

    @staticmethod
    def OpenWebUrl(url):
        """调用示例:url = Dto.Common.OpenWebUrl("www.baidu.com"),若允许下可用空字符串代替"""
        import webbrowser
        webbrowser.open(url)#用浏览器打开指定的路径

    @staticmethod
    def NewGuid():#返回一个guid
        import uuid
        return uuid.uuid4()

    @staticmethod
    def WriteFileXML(path, data):  # 写txt文件
        """调用示例:returnstring=Dto.Common.WriteFile("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}.xml")  # 创建文件路径和文件名
            with open(file_path, 'w', encoding="utf-8") as file:
                file.write(data)
            return f"""创建成功,时间:{datetime.now().strftime("%Y-%m-%d   %H-%M-%S")}"""
        except Exception as ex:
            return "出错了:" + str(ex)

    @staticmethod
    def ConvertToint(data, type="int"):
        # 调用示例:
        # data = Dto.Common.ConvertToint(data)  #data 是 string类型,一般看作是输入
        # if (data == -99):
        #   return 或者给个默认值data=0
        if (data == "" or data is None):
            return -99
        elif (type == "int"):
            return int(data)
        elif (type == "float"):
            return float(data)

    @staticmethod
    def ConvertToString(L,joinstr="\n"):
        '''调用示例:Dto.Common.ConvertToString(L)'''
        return joinstr.join(L)

    @staticmethod
    def TryToFloat(value):
        """尝试将字符串转换为浮点数,成功则返回浮点数,失败则返回原始字符串"""
        try:
            return float(value)
        except (ValueError, TypeError):
            return value

【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:如何给树和项目列表添加子节点

(1)添加的方法

【树节点添加】需要添加两个地方

注意这里item_0是父节点,item_1是子节点,一一对应的

 【项目列表】也是加两个地方,一个是初始化,一个是文字

(2)更好的建议

 不要一行一行复制,要会用循环(例如我有13个列表项目)

代码附上:

for index, text in enumerate([
    "01正则匹配", "02遍历工具", "03格式化JSON", "04行处理",
    "05前端模板", "06换行转换", "07手输字段", "08代码实体字段",
    "09变量名写法", "10目录树生成", "11列表计算器", "12随机数生成",
    "13随机时间生成"
]):
    item = self.listWidget.item(index)
    item.setText(_translate("Form", text))

Q2:坐标怎么看

含义:起始坐标+宽高

QRect(260, 10, 101, 31)

- 值越大越往右:260

- 值越小越往下:10

- 宽度:101

- 高度:31

Q3:禁止放大怎么设置

Form.setMaximumSize(Form.width(), Form.height())

Q4:如何设置Icon

Form.setWindowIcon(QIcon("ril.ico"))#设置icon

Q5:如何默认选中某些元素

类型 代码
选择按钮self.checkBox.setChecked(True) # 设置默认选中
项目列表self.listWidget.setCurrentRow(0) # 设置默认选中第一项

Q6:输入框如何显示提示语

self.plainTextEdit.setPlaceholderText(a)  #所有输入框通用

Q7 : 输入框如何清空内容

self.plainTextEdit.clear()  #所有输入框通用

Q8 :三元运算符在qt开发中经常用,请问是?

result = "无输出结果" if result == "" else result

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值