基于Qt的个人事务全自动化软件-DoQuick

起风了 Lv3

开发背景

作为班级团支书,班级里经常有一些表格数据收集、统计、分析的事物,比如每周都有“青年大学习”视频学习,我需要收集同学们的完成情况,并且统计、分析,及时提醒未完成的同学,最后还要做出报表反馈给老师。程序固定且繁琐,遂有了开发一款处理自己日常事务软件的想法,该软件应运而生。

image-20260824132320379

至于为什么起名叫DoQuick,顾名思义该软件就是为了帮助我更快的完成那些程序固定繁琐的任务,可以说是为了提高效率,因此命名为DoQuick(做的更快)。

image-20260824132440544

开发环境

  • 系统:Window 10
  • IDE:Pycharm + QtCreator
  • 语言:Python 3.11.2

需求

  1. 收集信息:我需要能收集到同学们的完成情况,并且还要尽可能及时。因为总不能最后一天才收集好,那样根本就不叫提醒了吧,应该叫“要你命”才对
  2. 分析 & 提醒:拿到名单了,我还要分析一下哪一些还没完成,然后还要去提醒这些同学。
  3. 报表反馈:然后每周结束,我需要把结果做成明了的报表反馈给老师。

于是乎,根据这几个需求,我想出了一条实现路径:

  1. 对于收集信息:我用金山云文档做一个类似问卷一样的在线表单,把链接发到群里,大家只需要打开链接,选择自己的学号,然后上传自己的截图就好了(截图是俺们头头的要求),但是收集到的数据,只能网页看,而且不能导出excel,还要肉眼去分辨,非常麻烦。img就是这种的,一个班级四十多个人,有时候你刚看完数据就又更新了,很麻烦。所以我需要一键获取实时数据,而且是未完成名单。所以就通过Python的爬虫脚本实现。思路大概如下:
    image-20260824133747601

  2. 对于分析和提醒:用上一步获取的数据,直接进行对比筛就行。提醒的话,我用的是QQ邮箱,因此还需要能一键获取未完成名单,并且一键发送提醒邮件。

  3. 对于报表反馈:因为报表的样式是一定的,区别只是数据不一样,因此我也打算通过Python做成一个程序,只需要一键就能导出现成的报表。

功能设计与实现

功能列表大概如下:

  1. 爬取信息:每周的信息收集都是通过金山文档进行的,金山文档可以直接查看结果,统计信息在金山云文档里,金山文档能很方便的查看哪些人没提交但是却不能导出名单,如图所示:
    所以需要借助爬虫获取完成名单。
  2. 筛选名单:有时需要完成同学的名单,所以需要一个可以直接筛选对应分类的功能。
  3. 一键提醒每位未完成的同学:群发基本不看,数据会很难看,所以需要单发。
  4. 自动化Excel事务:顾名思义,由于要需要提交数据表格,因此需要自动化生成表格,这种固定且繁琐的事务最适合自动化了。
  5. 自动打卡:其实这种脚本添加到系统计划里自动执行最方便了,但由于不是每天执行,而且每周我都会打开该软件,所以干脆就集成在这个软件里面了。

1. 爬取信息

利用Selenium库,可以很方便的实现,大概思路如下:

  1. 首先爬取所有元素里的text文本,也就是所有人的名单。
  2. 然后爬取已经完成的元素里的text文本,也就是已完成名单。


只需要输入链接,就能自动爬取。核心的爬取代码如下:

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
def analyze(self):
if self.check_url_available():
try:
# response = requests.get(self.convert_link())

driver = webdriver.Chrome(options=self.options, service=ChromeService(ChromeDriverManager().install()))
driver.get(self.convert_link())

more_btn = WebDriverWait(driver, 10).until(
lambda d: d.find_element(By.XPATH, "//span[contains(text(),'查看全部座位图')]"))
more_btn.click()

WebDriverWait(driver, 20).until(
lambda d: d.find_element(By.XPATH, "//div[contains(text(),'仅创建者可查看填写详情')]"))

unfinished_name_els = WebDriverWait(driver, timeout=10).until(lambda x: x.find_elements(By.XPATH,
'//div[@class="src-newform-pc-components-SolitaireSeat-index__seatItem"]'))

finished_name_els = WebDriverWait(driver, timeout=10).until(lambda x: x.find_elements(By.XPATH,
'//div[@class="src-newform-pc-components-SolitaireSeat-index__seatItem src-newform-pc-components-SolitaireSeat-index__used"]'))
for el in unfinished_name_els:
title_text = el.get_attribute('title')
uname = re.search(r'[\u4e00-\u9fa5]+', title_text).group()
uid = re.search(r'[0-9]+', title_text).group()
self.unfinished_dict.setdefault(uid, uname)

for el in finished_name_els:
title_text = el.get_attribute('title')
uname = re.search(r'[\u4e00-\u9fa5]+', title_text).group()
uid = re.search(r'[0-9]+', title_text).group()
self.finished_dict.setdefault(uid, uname)

for k, v in self.unfinished_dict.items():
self.total_dict.setdefault(k, v)

for k, v in self.finished_dict.items():
self.total_dict.setdefault(k, v)

self.unfinished_names = [re.search(r'[\u4e00-\u9fa5]+', y).group() for y in
[x.get_attribute('title') for x in unfinished_name_els]]
self.finished_names = [re.search(r'[\u4e00-\u9fa5]+', y).group() for y in
[x.get_attribute('title') for x in finished_name_els]]
self.total_names = list(set(self.unfinished_names).union(set(self.finished_names)))

except Exception as e:
pass
finally:
self.signals.finished.emit()

2. 筛选名单

这个利用前面爬取的信息可以很方便实现。只需要将爬取到的数据做交并差集就可以获得想要的名单。UI设计如下:

核心代码如下:
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
# 槽->点击【取全部】按钮
@Slot()
def click_get_total(self):
if self.ui.listWidget_totalNum.count() == 0 and self.ui.listWidget_unfinished.count() == 0:
QMessageBox.information(self, "提示", "请先进行分析~")
else:
self.ui.listWidget_result.clear()
# self.ui.listWidget_result.addItems(self.analyze_worker.total_names)
self.ui.listWidget_result.addItems([f"{v} {k}" for k, v in self.analyze_worker.total_dict.items()])

# 槽->点击【取已完成】按钮
@Slot()
def click_get_finished(self):
if self.ui.listWidget_totalNum.count() == 0 and self.ui.listWidget_unfinished.count() == 0:
QMessageBox.information(self, "提示", "请先进行分析~")
else:
self.ui.listWidget_result.clear()
# self.ui.listWidget_result.addItems(self.analyze_worker.finished_names)
self.ui.listWidget_result.addItems([f"{v} {k}" for k, v in self.analyze_worker.finished_dict.items()])

# 槽->点击【取未完成按钮】
@Slot()
def click_get_unfinished(self):
if self.ui.listWidget_totalNum.count() == 0 and self.ui.listWidget_unfinished.count() == 0:
QMessageBox.information(self, "提示", "请先进行分析~")
else:
self.ui.listWidget_result.clear()
# self.ui.listWidget_result.addItems(self.analyze_worker.unfinished_names)

self.ui.listWidget_result.addItems([f"{v} {k}" for k, v in self.analyze_worker.unfinished_dict.items()])

3. 批量发送邮件

要及时提醒未完成的同学,由于加有班级Q群,每位同学的qq号都能获取到,但是如何通知呢?单独发私聊是一件极其麻烦的事,转发的话又要加所有同学为好友才行,直接发群里的话确实省事,但是最终的数据都非常难看,到时候直接问责的可就是我这个团支书了,考虑了两种方案,一种是利用QQ Bot给同学发私聊消息,但是实际搭建使用后,由于无法给不是好友的qq发消息,这个方案作罢,另一个方案是通过班级群获取到每位同学的QQ号,并且与对应的同学做映射(毕竟qq好拿过来谁是谁也不清除),然后加上qq邮箱的尾椎 “qq@.com”即可得到email地址,然后群发email即可。

这一步的工作量主要在于信息收集,要收集【同学姓名<=>学号<=>qq】数据并存储到JSON文件里,QQ官方有个Q群管理web页面,可以用爬虫很方便的获取这些信息(前提是你得有班级群)。

获取到信息后,利用smtplib库发送邮件。

4.自动化Excel事务

这个利用Pandas和xlwings可以很方便的实现。

5. 自动打开

抓包并且分析后,向服务器发送完成封包即可,同时可以获取到图片链接。

软件演示截图

  1. 桌面图标

    UI设计风格模仿facebook了旧图标,采用PS设计,内层代表字母D,外层代表字母Q,是软件名DoQuick的首字母简称。

  2. 软件主界面,至少布局还凑活。

  • 标题: 基于Qt的个人事务全自动化软件-DoQuick
  • 作者: 起风了
  • 创建于 : 2024-03-13 14:48:40
  • 更新于 : 2026-09-26 11:50:56
  • 链接: https://www.wangcac.me/2024/03/13/基于Qt的个人事务全自动化软件-DoQuick/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论