PyQt5试验:基于QTableWidget的应用

PyQt5试验:基于QTableWidget的应用

前言:

本文内容为基于 QtableWidget 控件,结合时间管理四象限思想,制作一个简易的任务优先度计算器。 需先对 Python 语言和 PyQt5 库有一定了解,关于 PyQt5 的快速入门可阅读《PyQt5初学试验记录》(←点击)系列文章。

如今,是一个全类别大爆发的时代,我们每天都需要面对数不清的新生事物,或是要学习的各种知识,或是计划了的各项工作,尤为让人头疼的便是,自己该先做什么?市面上已有各式各样的时间管理 or 工作规划 APP,为苦于选择困难症和拖延症的人群,提供了理清思路的方法。

笔者在日常的学习工作中,亦是对将要处理的成批事务应该先做什么,而感到苦恼。在了解时间管理四象限的思想后,方才有了思路。下面,笔者将介绍一个用于安排任务的方法。
在这里插入图片描述
总之我就是拿东西试了一下 QTableWidget 的功能)时间管理四象限,如上图所示。以重要与紧急两种标签,将事务分为四类,可以令用户很明确地意识到紧急且重要的事情,是需要先做的。并且在事务项较多时,利于把控全局,规划各项事务的执行顺序。

根据这个概念,笔者在 EXCEL 中建立了一个小模型,依照不同的紧急、重要程度,设置参考的系数区间,另外再增加一个时间系数。根据用户对任务的判断填写3个系数,以此计算优先值。这里笔者所用的优先值算法是:紧急X0.6 + 重要X0.4 + 时间X0.1。即更紧急的事占更大的权重,时间作为一个辅助区分的调整值。
在这里插入图片描述

计算出优先值后再排序,即可得知执行任务的优先顺序了。(嗯,终于把写文章调到最优先了呢!
在这里插入图片描述

好,接下来用 pyqt5 把这玩意儿做出来吧!(明明在 EXCEL 里操作更快

要用魔法对付魔法,故要用表格实现表格,本次采用的是 QTableWidget 控件。首先在 Qt Designer 中拖拖拽拽把界面做出来,上面一排按钮为基本功能,下面的框即是核心控件 。
在这里插入图片描述
在这里插入图片描述

QTableWidget 继承自 QTableView,区别在于:儿子不够独立自主,只认识爸爸教的标准的数据模型;而爸爸则见多识广,可以使用奇形怪状的数据。所以这里选择教育不成熟的儿子(也就是说 QTableView 用着麻烦吧)。

首先修改 __init__ 方法,添加 colsrows 变量表示表格的列数、行数,在后续增删改操作中会使用到。接着设置每一列的列宽自适应 QTableWidget 的大小,因为除了任务项的内容是长度不确定的字符,其他列都是数值,即宽度基本可以不变,所以设置仅首列可手动调整列宽。

def __init__(self):
    super(ReLearnForm, self).__init__()
    self.setupUi(self)
    self.cols = self.tableWidget.columnCount()
    self.rows = 0 # 任务数
    self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # 自适应列宽
    self.tableWidget.horizontalHeader().setSectionResizeMode(0, QHeaderView.Interactive) # 仅首列可手动调整

在这里插入图片描述

下面开始实现各个按钮的功能:
1、Read me
点击打开一个自定义的说明书窗口,这里的 ReadForm() 为一个 Dialog 类。

def read(self):
    self.d = ReadForm()
    self.d.show()
    self.d.setWindowTitle("参数说明")

2、+ (增加任务)
rows 记录当前的任务行数。

def add_row(self):
    print('添加一行任务记录')
    self.rows += 1
    self.tableWidget.setRowCount(self.rows)

3、- (删除任务)
在删除行之前,需先获取当前选择的行;

__init__ 方法中添加 row_flag 变量用于记录被选中的行索引(第一行为 0),初值(未选择时)为 -1。通过 itemSelectionChanged 绑定单元格选择信号和自定义槽函数;

self.row_flag = -1 # 当前被选中的行索引
self.result = []
self.tableWidget.itemSelectionChanged.connect(self.chioce) # 单元格选择改变

自定义槽函数 choice() 实现对 row_flag 的修改;

def chioce(self): # 修改被选中的行索引
    self.row_flag = self.tableWidget.currentRow()
    print(f'选中第{self.row_flag + 1}行')

编写删除行的方法,删除后同时修改 rows,并重置 row_flag

def del_row(self):
    print('删除一行任务记录')
    if self.row_flag == -1:
        QMessageBox.about(self,'提醒','未选择要删除的行!')
    else:
        self.tableWidget.removeRow(self.row_flag) # 删除指定行
        self.rows -= 1
        self.row_flag = -1

4、X (清空任务)
将控件中的行数设为 0 即可实现清空。

def clean_rows(self):
    print('清空任务记录')
    reply = QMessageBox.question(self, '提示', '请确认是否要清空!', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
    if reply == QMessageBox.Yes:
        self.rows = 0
        self.tableWidget.setRowCount(self.rows)

5、RUN (计算)
在说明撤销操作之前,先来讲解 RUN 按钮所实现的功能;

__init__ 方法中添加 result 变量用于暂存计算结果;

self.result = []

实现计算优先度的方法:① 按行遍历,获取任务名称、紧急程度、重要程度、时间系数4列的输入值,依照前文所述的规则计算得到优先度 p (四舍五入保留2位小数),② 将 p 值添加进单元格并设为可选择、不可编辑,将该单元格放进同一行的第五列(索引4),即优先度一列中,③ 最后将每一行的结果存入列表,根据优先度进行降序,再通过 result 变量保存。

def prior_value(self):
    print('计算优先度')
    # print(f"共{self.row_nums}行{self.cols}列数据")
    list_para = []
    try:
        for r in range(self.rows):
            my_item = self.tableWidget.item(r, 0).text() # 任务名称
            urgent = int(self.tableWidget.item(r, 1).text()) # 紧急程度
            important = int(self.tableWidget.item(r, 2).text()) # 重要程度
            t = int(self.tableWidget.item(r, 3).text()) # 时间系数
            p = round(urgent*0.6 + important*0.4 + t*0.1,2) # 优先度
            item_p = QTableWidgetItem(str(p)) # 将优先度值添加为单元格
            item_p.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # 设置为可选择、不可编辑
            self.tableWidget.setItem(r, 4, item_p)
            list_para.append([my_item, urgent, important, t, p])
        self.result = sorted(list_para, key=lambda x: x[4], reverse=True) # 根据优先度将行元素列表降序
        self.sort_move(self.result)
    except:
        pass

在这里插入图片描述
此时计算出了结果,但仍未对单元格进行调整,故将排序后的结果列表 result 传入 sort_move() 方法进一步处理:从 result 中获取各列的值重写单元格,其中 now 为排序后的当前行索引。QTableWidget 不具备统一调整居中的功能,还需再次遍历所有单元格设置居中。

def sort_move(self, list_para_sorted): # 根据排序结果重写单元格
    print('执行了sort')
    for now in range(len(list_para_sorted)):
        item_it = QTableWidgetItem(str(list_para_sorted[now][0]))
        item_u = QTableWidgetItem(str(list_para_sorted[now][1]))
        item_im = QTableWidgetItem(str(list_para_sorted[now][2]))
        item_t = QTableWidgetItem(str(list_para_sorted[now][3]))
        item_p = QTableWidgetItem(str(list_para_sorted[now][4]))
        item_p.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)  # 设置为可选择、不可编辑
        print('得到了items')
        self.tableWidget.setItem(now, 0, item_it)
        self.tableWidget.setItem(now, 1, item_u)
        self.tableWidget.setItem(now, 2, item_im)
        self.tableWidget.setItem(now, 3, item_t)
        self.tableWidget.setItem(now, 4, item_p)
        print('设置了items')
    for i in range(int(self.rows)):  # 设置所有单元格文本居中
        for j in range(int(self.cols)):
            self.tableWidget.item(i, j).setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

最终结果如下,至此核心功能就已经实现了。但为了让这个(破玩意儿看起来更牛批)工具更完善,继续实现撤销删除和保存结果至 EXCEL 的功能。
在这里插入图片描述

6、← (左箭头 - 撤销)
result 变量中读入最新记录,先恢复行,再利用 sort_move() 方法恢复内容,这也是将 sort_move() 单独写成一个方法而不整合到 prior_value 方法的原因。

def return_result(self):
    print('撤销操作')
    print(self.result)
    if self.result:
        self.rows = len(self.result) # 恢复任务数
        self.tableWidget.setRowCount(self.rows) # 恢复行
        self.sort_move(self.result) # 恢复内容

注意: 删除无内容行的不会回复,即需当前 result 变量有内容才可以恢复。

在这里插入图片描述

7、↓ (下箭头 - 保存)
真是个鸡肋的功能)现在是 pandas 时间!若 result 不为空,则将其逐行写入 DataFrame 后保存至 EXCEL 文件,然后调用 os.startfile('.xlsx') 打开文件。

def save(self):
    print('保存结果')
    if self.result:
        # [my_item, urgent, important, t, p]
        df = pd.DataFrame(columns=['任务项', '紧急程度', '重要程度', '时间系数', '优先度'])
        for i in range(len(self.result)):
            df.loc[i] = self.result[i]
        df.to_excel('优先度结果表.xlsx', index = False)
        os.startfile('优先度结果表.xlsx')
    else:
        QMessageBox.about(self,'提醒','未产生计算结果!')

最后,打包成可执行程序,图标是笔者绘制的一只在思考的鸽子(咕咕,就算安排了任务我也不会按时做的)。关于打包程序可以参考《PyQt5初学试验记录(三):Pyinstaller打包小结》。
在这里插入图片描述

完成了这个小工具后,相信大家已对 QTableWidget 控件的特性有了一定的了解,把它运用到自己的小玩意儿中吧!

谢谢阅读!(源码已上传至gitub:https://github.com/SeonPan/ReLearn

猜你喜欢

转载自blog.csdn.net/zohan134/article/details/107238759