PyQt笔记
PyQt5.15.4(python 32bit)连接mysql 64位数据库
下载mysql-connector-c-6.1.6-win32.msi:版本太新的话,可能不含所需dll文件。
解压后的文件
F.lib.libmysql.dll
重命名为libmysql.dll
,放入python/Lib/site-packages/PyQt5/Qt5/bin文件夹中下载qsqlmysql.dll_Qt_SQL_driver_5.15.2_MSVC2019_32-bit.zip,其他版本/位数的dll详见:https://github.com/thecodemonkey86/qt_mysql_driver/releases)
解压后sqldrivers中的文件
qsqlmysql.dll
与qsqlmysqld.dll
两份文件,放入python/Lib/site-packages/PyQt5/Qt5/plugins/sqldrivers文件夹中Complete!
原因:每个PyQt版本都对应一个Qt版本,由于Qt在5.12.x更新后官方不提供mysql的dll,需要自行编译。
Pycharm Debug显示数据库driver无mysql,连接失败
设置-构建,部署,执行-Python调试器-P yQt兼容:自动改为Pyqt5
五分钟刷新一次表:https://stackoverflow.com/questions/60876325/python-pyqt5-table-with-sql-postgrees
QTableWidget
cellchanged/itemchanged 区别
cellChanged
is for all QAbstractItemView classes, itemChanged
is specific to the convenience views (QListWidget, QTableWidget, QTreeWidget) where you use the corresponding item classes to interact with the model.
ItemDelegate设置数字/空字符
要匹配数字或空字符串’’,即用户没有输入任何输入,请执行此操作
1 | (^[0-9]+$|^$) |
匹配数字、空字符串或空白字符
1 | (^[0-9]+$|^$|^\s$) |
designer.exe位置 -PyQt5与PySide2
1 | 两个designer.exe的文件大小一致,使用起来应该没有区别 |
PyInstaller打包PyQt应用程序
1 | -F, –onefile 打包成一个exe文件。 |
不同分辨率的显示器编译代码
- 不同分辨率的显示器编译PyQt程序,有的控件大小会变(在整个窗口是一种布局的情况下)
1 | if __name__ == '__main__': |
QDialog / QMainWindow 设置窗口和Win系统下的任务栏图标
- Designer下icon->选择资源->选择图片,图片位置与.ui文件在同层目录,
- pyinstaller打包之后,放在dist文件夹下的一级目录,可正常显示
- 代码中运行
self.setWindowIcon(QIcon("Path"))
Table View/Widget 设置列宽模式
宽/高分配模式
1 | # 列宽自动分配 |
样式设置示例
1 | # 随内容分配列宽 |
Table View[可绑定数据库模型]
Table View中使用自定义的数据模型来显示表格的内容,通过
setModel
来绑定数据源1
2
3df = pd.read_csv('file_path')
model = DataFrameModel(df)
self.tv_badn.setModel(model)
TableView自定义数据模型 - dataframe
关于自定义的DataFrame数据模型(不会写,copy的),自定义的函数关注
data
与headerData
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77# 没有序号的DataFrame - TableModel
class DataFrameModel(QAbstractTableModel):
def __init__(self, data):
QAbstractTableModel.__init__(self)
self._data = data
# 行数
def rowCount(self, parent=None):
return self._data.shape[0]
# 列数
def columnCount(self, parent=None):
return self._data.shape[1]
# 数据项的字段
def data(self, index, role=Qt.DisplayRole):
if index.isValid():
if role == Qt.DisplayRole:
return str(self._data.iloc[index.row(), index.column()])
return None
# 表头/行序号数据?
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self._data.columns[col]
return None
# 行序号从0开始的DataFrame - TableModel
class DataFrameModel(QAbstractTableModel):
def __init__(self, data):
QAbstractTableModel.__init__(self)
self._data = data
def rowCount(self, parent=None):
return self._data.shape[0]
def columnCount(self, parent=None):
return self._data.shape[1]
def data(self, index, role=Qt.DisplayRole):
string_role = str(self._data.iloc[index.row(), index.column()]).strip()
if index.isValid():
if role == Qt.ForegroundRole:
if pd.isna(self._data.iloc[index.row(), index.column()]):
return QColor("red")
elif string_role == 'FAIL':
return QColor("red")
elif str(self._data.iloc[index.row(), index.column()]).strip() == 'PASS':
return QColor("Green")
if role == Qt.DisplayRole:
return str(self._data.iloc[index.row(), index.column()])
return None
def headerData(self, section, orientation, role):
if role != Qt.DisplayRole:
return QVariant()
if orientation == Qt.Horizontal:
try:
return self._data.columns.tolist()[section]
except(IndexError, ):
return QVariant()
elif orientation == Qt.Vertical:
try:
return self._data.index.tolist()[section]
except(IndexError, ):
return QVariant()
return None
# 序号从1开始的headerData函数
def headerData(self, section: int, orientation, role: int) -> str:
# header data (first row and first column)
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
if isinstance(self._data.index[section], str):
return self._data.index[section]
else:
return str(self._data.index[section] + 1)
可能会用到的数据模型 | 含义 |
---|---|
QSqlQueryModel | 对SQL的查询结果进行封装 |
QSqlTableModel | 对SQL中的表格进行封装 |
QSortFilterProxyModel | 对模型中的数据进行排序或过滤 |
QStandardItemModel | 存储任意层次结构的数据 |
TableView设置委托:设置单元格不可编辑/输入类型卡控
1 | class EmptyDelegate(QItemDelegate): |
Sqlite3数据库连接、查询与显示
1 | class EmptyDelegate(QItemDelegate): |
Table Widget[可编辑/识别item字段]
Table Widget是Table View的子类,只能使用标准的数据模型(
QStandardItemModel
?),单元格数据通过Table Widget Item对象来实现。与Table View最大的不同除了数据模型固定以外,可以直接在表格视图中进行数据的修改
Table Widget的水平或垂直表头可以直接在designer中设定,也可以使用函数设定,在设定前,需要先初始化行号与列号
1
2
3
4tableWidget.setRowCount(3)
tableWidget.setColumnCount(3)
tableWidget.setHorizontalHeaderLabels(['姓名','性别','体重(kg)'])
tableWidget.setVerticalHeaderLabels(row_list)Designer中的
editTriggers
中可设定表格的编辑方式,可勾选NoEditTriggers
使其为禁止编辑的只读状态。
1 | # 表头为自适应的伸缩模式,可以根据窗口大小来改变网格大小 |
Line Edit
自动补全方式
1 | from PyQt5.QtWidgets import QCompleter |
- 匹配方式:completer.setFilterMode(
MatchMode
)- Qt.MatchStartsWith 开头匹配
- Qt.MatchContains 内容匹配(模糊匹配)
- Qt.MatchEndsWith 结尾匹配
- 补全模式:completer.setCompletionMode(
CompletionMode
)- QCompleter.PopupCompletion 弹出下拉框选项补全
- QCompleter.InlineCompletion 行内显示补全 (建议开头匹配)
- QCompleter.UnfilteredPopupCompletion 全显示选项补全
- 大小写是否敏感:completer.setCaseSensitivity(
SensitivityMode
)- QCompleter.CaseInsensitive 大小写不敏感
- QCompleter.CaseSensitive 大小写敏感
QCalendar
方法 | 描述 |
---|---|
setDateRange() | 设置日期范围选择 |
setMinimumDate() | 设置最小日期 |
setMaximumDate() | 设置最大日期 |
setSelectedDate | 设置一个QDate对象,作为日期控件所选定的日期 |
setFirstDayOfWeek() | 重新设置星期的第一天,默认是星期日,其参数枚举值[Qt.Monday, Qt.Tuesday, Qt.Wednesday, Qt.Thursday, Qt.Friday, Qt.Saturday, Qt.Sunday] |
selectedDate() | 返回当前选定的日期 |
minimumDate() | 获取日历控件的最小日期 |
maximumDate() | 获取日历控件的最大日期 |
setGridvisible() | 设置日历控件视图是否有网格线 |
QDate
1 | date = self.cal.selectedDate() |
QComboBox 下拉框
- 两个下拉框实现级联:父级CurrentIndexChanged信号,关联子级Items刷新
- addItems([‘str1’, ‘str2’, ‘str3’])
- QComboBox在designer中可以设置为可输入(Edit)的样式,输入的内容如果和选项模糊匹配会补齐。
复选框:自定义控件,ComboCheckBox
1 | """ |
QSpinBox 数字写入
方法 | 说明 |
---|---|
setMinimum() / setMaximum() | 设置计数器的max min边界值 |
singleStep() | 设置计数器的步长值 |
setRange() | 设置计数器的max min边界值+步长值 |
setValue() / Value() | 设置/获取计数器的当前值 |
QMessageBox设置弹窗信息:Open File,格式化超链接的绝对路径
1 | 'file:///F:/CSVEdit/test_data/CSV%E5%90%88%E5%B9%B6/001-1%E6%B1%87%E6%80%BB.csv' |
Designer动态加载 自定义控件
- 在Designer中右键选中的控件 - 提升为:选择对应基类相同的提升的类
- 新建提升的类:
- 基类名称
- 提升的类名称:Class 的类名称,eg.
CheckableComboBox
- 头文件:与相对路径有关,eg.
ui.CheckableComboBox
, 相对路径为ui/CheckableComboBox.py
工时统计软件设计思路简记
工作日志 (1) :老化日志(1) : 老化箱(1) ,如果涉及到一个工作日志用到多个老化箱,则将工作日志拆分为对应的条数
遇到这种情况需要解除添加任务时的卡控
老化日志(n) : 老化板(n) (中间表)
与一个工作日志绑定的id,由于与多个老化板关联,因此需要生成多条老化日志