1框架选择

比较tk、pyqt、pyside之后,选择pyqt。主要理由是,pyqt开发实例多,文档全,成熟,后期发展空间大,而且跨平台也不差,语法与pyside差不多,后期也容易切换。
参考文献:https://www.zhihu.com/question/32703639/answer/2801830233
那么用pyqt5还是pyqt6呢?
pyqt6。因为是升级迭代产品,更优秀。也不是最新beta版本,已经有pyqt6.2了,所以很多问题应该也解决了。

2安装包

需要安装pyqt6、pyqt6-tools、等
参考文献:https://blog.csdn.net/qq_37529913/article/details/127944429
在Pycharm中配置Designer.exe
参考文献:
https://blog.csdn.net/weixin_43439916/article/details/122221459
https://blog.csdn.net/qq_37529913/article/details/127944723
注意:这里会报错

ImportError: DLL load failed while importing QtGui: 找不到指定的程序。

原因是:开始安装pyqt6默认是6.4版本,一共安装了pyqt6、PyQt6-Qt6、PyQt6-sip三个包,而后来安装pyqt6-tools默认是6.1版本,虽然安装过程中更新了pyqt6的版本为6.1.0,但是PyQt6-Qt6版本还是6.4,所以导致出错。
解决方法:把pyqt6、PyQt6-Qt6、PyQt6-sip全部卸载,再重新安装指定版本。

(venv) D:\mypython\guiyian\venv\Scripts>pip install PyQt6==6.1.0
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting PyQt6==6.1.0
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/ec/a6/f7807ca2d41b99a451fab83eee4f2f8afab3272cdbc82a17a2a093c12426/PyQt6-6.1.0-cp36.cp37.cp38.cp39-none-win_amd64.whl (5.3 MB)
Collecting PyQt6-Qt6>=6.1.0
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/c3/84/4731f1d9c6347a02e08609bdc00fd9c9b2b27333f0f0236bacf6442eede7/PyQt6_Qt6-6.4.2-py3-none-win_amd64.whl (50.6 MB)
Collecting PyQt6-sip<14,>=13.1
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/7d/da/371f93cc33f6d3ace9d8911964f8acc7c286d66f95a0c811e3a65552743b/PyQt6_sip-13.4.1-cp38-cp38-win_amd64.whl (72 kB)
Installing collected packages: PyQt6-sip, PyQt6-Qt6, PyQt6
Successfully installed PyQt6-6.1.0 PyQt6-Qt6-6.4.2 PyQt6-sip-13.4.1

发现没用,默认还是装的原来最新版本,只能卸载PyQt6-Qt6,指定安装6.1版本:

pip uninstall PyQt6-Qt6
pip install PyQt6-Qt6==6.1.0

2.2 决定用PyQt5

因发现PyQt6的资料较少,有些问题不好解决,决定使用成熟、资料较多的PyQt5,因为一般我能遇到的问题,大部分人都已经遇到并解决了。另外就是,本软件主要是为了效率,不需要太新技术,能解决问题就好。
在Pycharm中配置Pyqt5工具
参考文献:https://blog.csdn.net/yuanchenglei/article/details/124936528

3连接Access数据库

安装pyodbc包

import pyodbc
        # 连接mdb文件(根据实际地址)
        DBfile = r"D:\mypython\guiyian\data\jingfangyian.accdb"
        # 连接驱动
        conn = pyodbc.connect(
            r"Driver={Microsoft access Driver (*.mdb, *.accdb)};DBQ=" + DBfile + ";Uid=;Pwd=;charset='utf-8';")
        # 创建游标
        cur = conn.cursor()
        # 查询表
        sql = 'SELECT * FROM 国医大师经方验案精选;'
        cur.execute(sql)
        # 返回得到的数据集合
        result = cur.fetchall()
        # 遍历打印数据
        for row in result:
            # 展示个字段的值
            print(row)
            print(row[1], row[2])

但是代码运行报错:

pypyodbc.Error: ('IM002', '[IM002] [Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序')

原因是系统没有安装对应的ODBC驱动,需要下载安装:

Microsoft Access 2010 数据库引擎可再发行程序包

https://www.microsoft.com/zh-CN/download/details.aspx?id=13255

安装之后再运行,就没有问题了。

4 PyQt5布局管理

参考文献:
https://blog.csdn.net/A642960662/article/details/123093510

5 表格控件tableWidget的使用

参考文献:
https://blog.csdn.net/ungoing/article/details/127194935

5.1排序

先设置点击表头可以排序

self.tableWidget.setSortingEnabled(True) # 点击表头可以按拼音排序

第一列是id,把第一列按照数字排序

        for i in range(row):  # 遍历行
            for j in range(vol):  # 遍历列
                if j == 0:#第一列按照数字排序;其他列按照拼音排序
                    data = QTableWidgetItem()
                    data.setData(QtCore.Qt.DisplayRole, int(result[i][j]))
                    self.tableWidget.setItem(i, j, data)
                    continue

                data = QTableWidgetItem(str(result[i][j]))  # 转换后可插入表格
                # print(result[i][j])
                self.tableWidget.setItem(i, j, data)

5.2 tableWidget中添加回车事件响应函数

在当前行回车,直接响应双击函数。tableWidget中响应回车事件,需要先添加一个key过滤器,然后再重写过滤器对应的函数。

        # 设置tableWidget回车响应事件:1 添加事件过滤器installEventFilter 2 重定义eventFilter函数
        self.tableWidget.installEventFilter(self)

        # 设置小图标
        MainWindow.setWindowIcon(QIcon("./title.ico"))

    def eventFilter(self, source, event):
        if (event.type() == QtCore.QEvent.KeyPress and
                event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter)):
            # ensure that the table receives the key event first
            # res = super().eventFilter(source, event)
            # current = self.tableWidget.currentIndex()
            # nextIndex = current.sibling(current.row() + 1, current.column())
            # if nextIndex.isValid():
            #     self.tableWidget.setCurrentIndex(nextIndex)
            #     self.tableWidget.edit(nextIndex)
            # return res
            self.open_jiaodui()
        return super().eventFilter(source, event)

6 控件事件响应与信号槽(很重要)

问题:当我的main入口程序写在另一个py文件中时,自定义的槽函数死活都没有响应,只有系统的槽函数有响应,百思不得其解。
解决:当我无意中把main函数放在.ui对应的.py文件中时,一切问题消失。
原因:应该是主程序中没有捕获到对应的事件。

事件(Events)是 GUI 程序中很重要的一部分,它由用户操作或系统产生。当我们调用程序的 exec_()方法时,程序就会进入主循环中。主循环捕获事件并将它们发送给相应的对象进行处理。

为此,Qt引入了一种独一无二的处理模式:信号与槽机制。信号和槽可以说是 Qt 的精髓所在。

信号和槽是一种高级接口,应用于对象之间的通信,它是 QT 的核心特性,也是 QT 区别于其它工具包的重要地方。它为高层次的事件处理自动生成所需要的附加代码。在我们所熟知的很多 GUI 工具包中,窗口小部件 (widget) 都有一个回调函数用于响应它们能触发的每个动作,这个回调函数通常是一个指向某个函数的指针。但是,在 QT 中信号和槽取代了这些凌乱的函数指针,使得我们编写这些通信程序更为简洁明了。

所有从 QObject 或其子类 ( 例如 Qwidget) 派生的类都能够包含信号和槽。当对象改变其状态时,信号就由该对象发射 (emit) 出去,这就是对象所要做的全部事情,它不知道另一端是谁在接收这个信号。这就是真正的信息封装,它确保对象被当作一个真正的软件组件来使用。槽用于接收信号,但它们是普通的对象成员函数。一个槽并不知道是否有任何信号与自己相连接。而且,对象并不了解具体的通信机制。你可以将很多信号与单个的槽进行连接,也可以将单个的信号与很多的槽进行连接,甚至于将一个信号与另外一个信号相连接也是可能的,这时无论第一个信号什么时候发射系统都将立刻发射第二个信号。

简而言之:

信号(singal)与槽(slot)用于对象相互通信。

信号:当某个对象的某个事件发生时,触发一个信号

槽:响应指定信号的所做的反应

信号与槽构造了一个强大的部件编程机制,这种机制很多程度提高了类的封装性和完整性。
参考文献:
https://blog.csdn.net/xmnathan/article/details/51005789
https://blog.csdn.net/Prince999999/article/details/128950109

7 弹出窗口

7.1弹出子窗口

在主程序中点击button按钮吗,响应一个槽函数,在槽函数中创建子窗口的类,然后再显示出来。

    self.pushButton_3.clicked.connect(self.open_jiaodui)
    def open_jiaodui(self):
        self.jiaodui_window = jiaodui.Ui_JiaoduiWindow()
        self.jiaodui_window.show()

子窗口用qt5 designer设计为ui后,再自动转化为py代码,然后需要稍微修改一些代码:首先修改类名,默认的类名跟主程序类名一样,需要修改;其次,继承类改成QMainWindow;第三,添加构造函数。

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMainWindow

class Ui_JiaoduiWindow(QMainWindow):
    # 构造方法
    def __init__(self):
        super(Ui_JiaoduiWindow,self).__init__()
        # self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint)  # 只显示关闭按钮
        self.setupUi(self)# 初始化窗体设置

    # 自动生成的代码,用来对窗体进行设置
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        ...

调试中遇到一个小问题:子窗口中如果有错误,Pycharm中没有显示,而是直接程序关闭了,找不到错误在哪里,这样非常不利于程序调试。
解决方法:在run/debug configure中,把Emulate terminal in output console勾上后点击apply应用。
参考文献:https://blog.csdn.net/qq_40268672/article/details/114951320

7.2弹出消息框

        if tt in self.text_all_zz:
            QMessageBox.question(self, "Warning", "已经存在此症状!",
                                 QMessageBox.Ok )

参考文献:
https://blog.csdn.net/weixin_48668114/article/details/127097484

8 QListWidget

建议不要用QListView,因为只有view的显示部分,没有model,需要自己添加,比较麻烦。可以用QListView的子类QListWidget,自动集成了这些。

# 数据填充
    def data_fill(self):
        self.textEdit.setFontPointSize(11) # 设置字体大小。***要在setText之前设置才有用***
        self.textEdit.setText(self.yian_select.yian)
        self.lineEdit.setText(self.yian_select.laiyuan)
        self.lineEdit_2.setText(self.yian_select.yijia)
        self.lineEdit_5.setText(self.yian_select.zhengxing)
        self.lineEdit_8.setText(self.yian_select.fangji)
        self.textEdit_6.setText(self.yian_select.zhengzhuang)
        self.textEdit_7.setText(self.yian_select.tiqudefangyao)
        self.textEdit_8.setText(self.yian_select.fangyao)

        #
        self.qlist = self.zz.df["一级"].unique()
        self.listWidget.addItems(self.qlist) # 添加数据进列表
        self.listWidget.itemClicked.connect(self.clickList_2)

    # 触发二级list更新
    def clickList_2(self,item):
        self.item1 = item
        print("选择的是:",item.text())
        self.qlist2 = self.zz.df[(self.zz.df["一级"] == item.text())]["二级"].unique()
        print(self.qlist2)
        self.listWidget_2.clear()
        self.listWidget_2.addItems(self.qlist2)
        self.listWidget_2.itemClicked.connect(self.clickList_3)

    # 触发三级list更新
    def clickList_3(self, item):
        self.item2 = item
        print("选择的是:", item.text())
        self.qlist3 = self.zz.df[(self.zz.df["一级"] == self.item1.text()) & (self.zz.df["二级"] == self.item2.text())]["三级"].unique()
        print(self.qlist3)
        self.listWidget_3.clear()
        self.listWidget_3.addItems(self.qlist3)
        self.listWidget_3.itemClicked.connect(self.clickList_4)

    # 触发四级list更新
    def clickList_4(self, item):
        self.item3 = item
        print("选择的是:", item.text())
        self.qlist4 = self.zz.df[(self.zz.df["一级"] == self.item1.text()) & (self.zz.df["二级"] == self.item2.text()) & (self.zz.df["三级"] == self.item3.text())][
            "四级"].unique()
        print(self.qlist4)
        self.listWidget_4.clear()
        self.listWidget_4.addItems(self.qlist4)

参考文献:

https://www.yii666.com/blog/39084.html

https://blog.csdn.net/qq_45769063/article/details/124946712

8.2QTextEdit光标移到最后

这样还有一个问题,在重新给Qtextedit设置数据的时候,光标位置自动归零,跑到了最前面,然后在追加数据的时候,就是从最前面追加,不符合逻辑。需要将光标设置到末尾。

            cursor = self.textEdit_6.textCursor()
            cursor.movePosition(QTextCursor.End)  # 移动到末尾
            self.textEdit_6.setTextCursor(cursor)  # 设置光标到末尾

8.3 listWidget添加右键菜单

        # listWidget_5添加右键
        self.listWidget_5.setContextMenuPolicy(3)
        self.listWidget_5.customContextMenuRequested[QPoint].connect(self.listWidgetContext)

    # listWidget_5添加右键响应函数
    def listWidgetContext(self, point):
        popMenu = QMenu()
        # popMenu.addAction("添加")
        # popMenu.addAction("修改")
        popMenu.addAction(QAction(u'删除 ', self, triggered=self.delete_fangji))
        popMenu.exec_(QCursor.pos())

9 list保持原顺序去重

        ll = self.text_all_zz.split("、")[:-1]
        l2 = sorted(set(ll), key=ll.index)# 保持原顺序不变

参考文献:

https://zhuanlan.zhihu.com/p/589304039

10 Pandas DataFrame一些操作

10.1 删除行

#删除行
            row = self.zz.df[(self.zz.df["四级"] == self.listWidget_4.currentItem().text())]
            print(row)
            self.zz.df.drop(index=row.index, inplace=True)

参考文献:
https://geek-docs.com/pandas/pandas-dataframe/how-to-drop-row...

11QLineEdit自动补全

参考下面的方法实现:

# 联想列表
list =['python','c','java']
    def auto_lineEdi():
        """
        自动补全函数
        """
        # 设置匹配模式  有三种: Qt.MatchStartsWith 开头匹配(默认)
        #                      Qt.MatchContains 内容匹配
        #                      Qt.MatchEndsWith 结尾匹配
        self.completer = QtWidgets.QCompleter(list)
        self.completer.setFilterMode(Qt.MatchContains)
        # 设置补全模式  有三种: QCompleter.PopupCompletion 弹出选项补全(默认)
        #                      QCompleter.InlineCompletion 行内显示补全
        #                      QCompleter.UnfilteredPopupCompletion 全显选项补全
        self.completer.setCompletionMode(QtWidgets.QCompleter.PopupCompletion)

        # 设置 lineEdit的补全器
        self.lineEdit.setCompleter(self.completer)

参考文献:
https://blog.csdn.net/qq_32618327/article/details/120907487

12 设置任务栏栏小图标和标题栏小图标

        # 设置小图标
        MainWindow.setWindowIcon(QIcon("./title.ico"))

但是,在主窗口中没有问题,而在子窗口弹出后,任务栏的图标是Python的那个图标,怎么设置也无法改变。
image.png这是主窗口的任务栏图标,没有问题。
image.png这是弹出的子窗口任务栏,变成了Python默认图标了。
解决方法:在子窗口源码中加入如下代码:

import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("myappid")

image.png图标没有问题了,但文字还是Python!
参考文献:https://blog.csdn.net/Ariadna/article/details/121293654

13 连接MySQL数据库

13.1MySQL连接代码

    with open("db.ini",mode="r", encoding="utf-8") as f:
        db_info = json.load(f)
    import pymysql
    conn = pymysql.connect(
        host=db_info["host"],
        user=db_info["user"],
        password=db_info["password"],
        database=db_info["database"],
        port=3306,  # 数字3306
        charset='utf8',  # 不是utf-8
        autocommit=True  # autocommit=True 让每次提交都去调用 commit 函数 即更新事务
    )  # 连接数据库,
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)  # cursor=pymysql.cursors.DictCursor 表示返回结果以字典形式返回(不加为元祖形式)

参考文献:

https://blog.csdn.net/fenglepeng/article/details/108321285

13.2 limit语法

MySQL不支持top 100类似语法,而支持limit语法,更强大。

SELECT * FROM tb_students_info LIMIT 0,10

LIMIT 初始位置,记录数
LIMIT 关键字可以指定查询结果从哪条记录开始显示,显示多少条记录。

“初始位置”表示从哪条记录开始显示;

“记录数”表示显示记录的条数。

第一条记录的位置是 0,第二条记录的位置是 1。后面的记录依次类推。

注意:LIMIT 后的两个参数必须都是正整数。
参考文献:https://www.lsjlt.com/news/69321.html
https://blog.csdn.net/qq_47452807/article/details/122997700

14 登录界面

开始准备自己一个人校对,就没有设计登录界面,后来发现数据很多,校对任务比较重,需要多人进行校对,每人分配一部分任务,于是设计登录界面。Ui_login_MainWindow是登录界面的Ui代码,没有放上来。

# 登录界面
class login_window(QtWidgets.QMainWindow, Ui_login_MainWindow):
    def __init__(self):
        super(login_window, self).__init__()
        self.setupUi(self)  # 创建窗体对象
        self.init()
        self.admin = ""
        self.Password = "000"

    def init(self):
        self.pushButton.clicked.connect(self.login_button)  # 连接槽

    def login_button(self):
        if self.lineEdit.text() == "":
            QMessageBox.warning(self, '警告', '密码不能为空,请输入!')
            return None
        user = yianModel.User(self.lineEdit_2.text().strip())
        # if  self.password == self.lineEdit.text():
        # if (self.lineEdit.text() == self.Password) and self.lineEdit_2.text() == self.admin:
        if user.password == self.lineEdit.text().strip():
            # Ui_Main = Open_Camera()  # 生成主窗口的实例
            # 1打开新窗口
            mainui.setupUi(formObj, user)
            formObj.show()
            # 2关闭本窗口
            self.close()
        else:
            QMessageBox.critical(self, '错误', '密码错误!')
            self.lineEdit.clear()
            return None

if __name__ == "__main__":
    from PyQt5 import QtCore
    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)  # 自适应分辨率
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = login_window()
    formObj = QtWidgets.QMainWindow()  # 注意,这里和我们一开始创建窗体时使用的界面类型相同
    mainui = Ui_MainWindow() # 登录后的主窗口
    # window.setupUi(formObj)
    window.show()
    sys.exit(app.exec_())

参考文献:

https://blog.csdn.net/weixin_42795788/article/details/124364982

15 设置Tab顺序

PyQt5.QtWidgets.QWidget.setTabOrder
代码示例:

        # 设置tab顺序
        self.centralwidget.setTabOrder(self.lineEdit_2,self.lineEdit)
        self.centralwidget.setTabOrder(self.lineEdit,self.pushButton)

注意:是QWidget类的setTabOrder()方法。
参考文献:https://blog.csdn.net/qq_44880255/article/details/106389447?y...

16 配置MySQL公网远程访问

两种方法:1 如果你有公网ip,那就没的说,直接映射端口就行;2 大部分人的没有公网ip,可以用花生壳免费代理一个映射,很方便,当然,如果觉得业务量大,可以升级使用其vip,能享受更多带宽和流量。
下面是花生壳的官网,下载安装,使用非常简单。
https://hsk.oray.com/download/?utm_source=baidu&utm_medium=cp...

17 打包PyQt5程序

使用pyinstaller打包,可以将源多个文件、ico图标文件打包,同时会将源文件包含的模块自动一起打包。打包后的exe文件在当前项目的dist目录下。
多个文件打包格式如下:

pyinstaller [主文件] -p [其他文件1] -p [其他文件2] --hidden-import [自建模块1] --hidden-import [自建模块2]
(venv) D:\mypython\guiyian>pyinstaller -F -i title.ico -w main5_yian.py -p login.py -p yianModel.py -p jiaodui_ui.py --hidden-import login --hidden-import yianModel --hidden-import jiaodui_ui

打包过程中,遇到一个错误,ico文件打包时以来的模块PIL没有

Traceback (most recent call last):
  File "D:\mypython\guiyian\venv\lib\site-packages\PyInstaller\building\icon.py", line 50, in normalize_icon_type
    from PIL import Image as PILImage
ModuleNotFoundError: No module named 'PIL'

解决方法:安装pillow模块
pip install pillow

打包成功:
image.png
打包后的exe文件在当前项目的dist目录下:
image.png
文件有130M,不小啊
参考文献:https://blog.csdn.net/m0_47682721/article/details/124008653
https://blog.csdn.net/weixin_44037416/article/details/96842058


1 声望0 粉丝

引用和评论

0 条评论