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的那个图标,怎么设置也无法改变。
这是主窗口的任务栏图标,没有问题。
这是弹出的子窗口任务栏,变成了Python默认图标了。
解决方法:在子窗口源码中加入如下代码:
import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("myappid")
图标没有问题了,但文字还是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
打包成功:
打包后的exe文件在当前项目的dist目录下:
文件有130M,不小啊
参考文献:https://blog.csdn.net/m0_47682721/article/details/124008653
https://blog.csdn.net/weixin_44037416/article/details/96842058
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。