1. 简述
上一篇文章讲述了新界面的搭建思路以及如何进行不同界面的切换。本来关于如何使用代码来进行修改图片和文字是想在最后一篇文章进行介绍,但基于可能同学们对这方面有兴趣,下面会给出简单的示例,而具体的操作还是会放在最后一篇文章里面讲解。本篇文章还会讲一下“武汉大学建筑知识系统”的数据库构造。
2. 用代码修改图片和文字
-
修改图片
I. 修改普通图片(如后缀为.png
,.jpg
等类型的图片)
用到setPixmap
方法,而图片对象用QPixmap
获取:# 修改静态图片(图片地址最好采用相对路径,绝对路径很大可能不可用于其他电脑,因为别人的用户名不会和你是一样的) self.ui.label_pre.setPixmap(QPixmap(f"./images/buildings/{content[1]}.jpg")) self.ui.label_pre.setMaximumSize(QSize(711, 400)) # 设置一下图片显示大小
II. 修改动图(如后缀为
.gif
类型的图片)
用到setMovie
方法,而动图对象用QMovie
获取:self.movie = QMovie("./images/background/loading.gif") # 获取动图 self.ui.label_pre.setMaximumSize(QSize(124, 124)) # 设置一下图片显示大小 # 修改动态图片(图片地址最好采用相对路径,绝对路径很大可能不可用于其他电脑,因为别人的用户名不会和你是一样的) self.ui.label_pre.setMovie(self.movie) self.movie.start() # 启动动图播放(不加这句话将不显示动图)
-
修改文字
I. 方法一(不推荐,用的是pyuic5
转换后的提供代码进行改写)
用到setHtml
方法,感觉有些麻烦,当时第一次接触所以用了这个方法:_translate = QCoreApplication.translate self.ui.textBrowser.setHtml(_translate("Form", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n" "p, li { white-space: pre-wrap; }\n" "</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n" f"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:12pt; font-weight:600;\">建筑名</span><span style=\" font-size:12pt;\">:{content[2]}</span></p>\n" f"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:12pt; font-weight:600;\">类 别:</span><span style=\" font-size:12pt;\">{content[3]}</span></p>\n" "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:12pt; font-weight:600;\">简 介:</span></p>\n" f"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:12pt;\"> {content[4]}</span></p>\n" f"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:12pt; font-weight:600;\">地 址:</span><span style=\" font-size:12pt;\">{content[5]}</span></p></body></html>"))
II. 方法二(推荐)
用到setText
方法,少写了一些代码,而实现的功能是完全一样的,除了textBrowser
,Label
以及Button
的文字修改也是用setText
方法,大家要熟练掌握:self.ui.textBrowser.setText("<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n" "p, li { white-space: pre-wrap; }\n" "</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n" f"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:12pt; font-weight:600;\">建筑名</span><span style=\" font-size:12pt;\">:{content[2]}</span></p>\n" f"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:12pt; font-weight:600;\">类 别:</span><span style=\" font-size:12pt;\">{content[3]}</span></p>\n" "<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:12pt; font-weight:600;\">简 介:</span></p>\n" f"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:12pt;\"> {content[4]}</span></p>\n" f"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:12pt; font-weight:600;\">地 址:</span><span style=\" font-size:12pt;\">{content[5]}</span></p></body></html>")
注:上述文字用的是
HTML
的样式,因为默认的字体大小以及样式不好,大家可以自学HTML
等网页语言来美化文字,当然也可以直接在designer
里面修改文字获取HTML
样式!下面给出如何在designer
里面获取HTML
样式:先在拖出的
textBrowser
控件上面“右键”,选择“改变HTML
”(或者改变多信息文本),修改好格式:
然后点击源,就有相应的HTML
代码了,但是不建议在这里进行复制,建议通过pyuic5
命令把ui
文件转为py
文件后,在py
文件里面进行复制,因为py
文件会把格式给你转换好(比如字符串里面有单引号、双引号时会帮你加上转义字符,不用手动增加)。
但是这种是不是感觉有点麻烦,如果有很多文本框都需要修改,那么就要加入很多这种很长的语句,其实还有一种方法,就是自定义控件。只要在designer
中提升控件,这样就不用一个个设置了,包括控件的QSS
样式也不用一个一个在designer
里面设计,同时自定义控件还可以在以后的软件中重复使用。但是自定义控件对于小白来说不好掌握(难点),所以这里就不再介绍,感兴趣的同学可以自行学习!
3. 数据库 sqlite3
数据库 sqlite3
是大家电脑自带的一个数据库,无须下载安装,当然其有一定的缺点:任何人拿到你的数据库文件都可以修改,没有安全性可言,不过对于我们的这种不涉及用户隐私的软件就不用用到太高级的数据库了。如果大家是做签到系统那个项目的话,我建议不要用这个数据库,可以用 MySQL
这样安全性高的数据库,在听展示的时候,做这个项目的小组没介绍他们使用的数据库,所以我也不清楚他们用的是什么数据库。
那么有同学会问为什么要用数据库呢?我用 Excel
或者 txt
不行吗?答案当然是可以的!但是一旦数据量太大,Excel
的打开速度将会很慢(我实测过,在打开大小为 8 MB
的“森林背词”词汇表时,用时大概是5秒)。不过对于我们这种数据信息不多的时候用什么进行存储数据都可以。
下面只对我们项目用到的数据库增查操作来介绍,删改操作部分感兴趣的同学可自行了解(其实就是 SQL
语句的变化,主要的 python
代码操作是差不多的)。
-
创建数据库
我们用sqlite3.connect(数据库名称【包括后缀】)
来连接一个指定数据库,如果没有检测到这个数据库,它会帮你创建一个空的数据库。用cursor()
方法对数据库进行操作(相当于实例化一个对象吧)。execute()
方法是指对数据库要做哪些操作命令,里面一般输入的是SQL
语句。为节省篇幅,这里不再介绍SQL
语句怎么写。commit()
方法是指提交事务,sqlite3
在Python
中的操作都是以事务进行的。close()
方法是关闭数据库,这句话不能少,要养成习惯,每次用完数据库要关闭!确定要显示的建筑信息后,我确定了要存储的内容为:
id
(主键),name_pre
(建筑简称——模型预测时的输出),name_cn
(建筑全名),category
(所属类别——食堂、图书馆、教学楼、院楼、体育馆等建筑类别),introduction
(建筑简介),location
(地址),location_url
(地址链接——用于导航)。下面给出我们的数据库创建的代码:
def buildDatabase(): """ 创建数据库:buildings.db :return: NULL """ conn = sqlite3.connect('buildings.db') # 如果没有这个数据库,则创建这个数据库,然后连接数据库 cur = conn.cursor() # 对数据库操作就是对cur操作 # 数据库定义各属性(创建一个表) cur.execute(''' CREATE TABLE IF NOT EXISTS buildings( id INT not null, name_pre TEXT not null, name_cn TEXT not null, category TEXT not null, introduction TEXT not null, location TEXT not null, location_url TEXT not null, PRIMARY KEY (id));''') conn.commit() # 保存数据库(因为新建了一个表) conn.close() # 关闭数据库
-
增加数据
增加数据大部分代码和创建数据库差不多,不同的地方是插入之前需要判断数据库里面是否已经有要插入的内容(防止重复插入数据)。那么有很多方法来检测是否有重复插入的数据,我采用的是一种比较简单,但是不合规范的策略!就是按照插入数据的id
与数据库的行数进行比较,如果要插入的数据id
小于行数,则说明数据库里面已经有这个数据,那么停止插入;否则就插入新数据。但是有个
bug
,这个bug
主要是由人为原因引起。因为我是先把数据存在Excel
里面,再存储到数据库里面,那么如果我在Excel
开头新增了一个数据,那么到数据库这边认为id
为0
的数据存在,反而最后一条旧数据因为前面新增了一条数据,它的id
加一,此时数据库会将这条旧数据误以为新数据插入到数据库中。即新数据没插入,旧数据重复插入,但是因为数据库是我自己创建更新的,所以我可以手动删除数据库,重新运行程序就会生成正确的数据库。下面给出几种解决此问题的方法供大家参考与实践:
(1)将上面我的手动操作改为自动操作;
(2)新增数据库改操作,结合查操作修改内容,但是这种方法时间复杂度高,不建议用于大型项目;
(3)那就是从我们自己出发,在新增Excel
内容时,必须从末尾添加数据。到这里不知道有没有同学有疑惑呢?就是我既然用了
Excel
存储数据,为什么又用数据库存储一遍呢,这样不是相当于有2
份数据了吗?有这个疑问的同学很好,因为你学会了思考,其实在我上传的源代码里面确实有2
份一模一样的的数据。因为这是课程项目,所以我会把所有操作都尽量保存下来,但是在真正开发的过程中,我是不会把Excel
的内容发给你的,我只会把生成好的数据库文件(buildings.db
)发给你。下面是插入数据的相关代码:
def insertValue(content): """ 向数据库中插入内容 :param content: 是一个列表,为要插入的数据内容 :return: NULL """ conn = sqlite3.connect('buildings.db') # 连接数据库 cur = conn.cursor() # 对数据库进行操作 cur.execute('''SELECT COUNT(*) FROM buildings;''') # 计算当前数据库已有数据行数 row_count = int(cur.fetchone()[0]) # 返回当前数据库已有数据行数 if content[0] >= row_count: # 如果插入的数据不在数据库中(大于数据库行的数据),则允许插入 # 此处存在bug,即若数据在原数据excel中间插入,无法在新数据库中插入此条信息,而是插入最后一条信息(重复插入) # 不过由于此数据库由我们自行创建,所以可以人为避免此情况发生(不清楚是否重复插入时,可以将.db文件删除,再运行此脚本,将产生新的正确的数据库) # 插入 id, name_pre, name_cn, category, introduction, location, location_url cur.execute('''INSERT INTO buildings( id, name_pre, name_cn, category, introduction, location, location_url) VALUES (?, ?, ?, ?, ?, ?, ?);''', content) conn.commit() # 保存数据库 conn.close() # 关闭数据库
拓展:
fetchone()
指只取出一条符合条件的数据;要取出全部符合条件的数据可以用fetchall()
;要取出不多于指定数目且符合条件的数据可以用fetchmany(size)
,size
是指定不多于此数的数据数目。 -
查看数据
接下来到查看数据步骤。在查看数据前,我们必须确保数据库是存在的,所以我会先运行数据库插入数据的操作(即run()
)。但是在真正的项目开发里面,验证过程不是输入数据,而是判断这个叫buildings.db
的数据库是否存在。那么这个判断操作怎么实现呢?给你们一个思路:通过os
模块获取指定目录的文件名,看文件名是否在指定的路径里面,如果不存在,那一定用户删除了(因为软件开发好后,所有的内容都是打包好的,那么数据库文件是一定存在的)。比如我在开发“森林背词”的时候,数据一般是放在文件夹
db
里面,那么这时候只要检测文件夹db
里面有没有我要运行的数据库文件就可以了。
下面给出“武汉大学建筑知识系统”的查看数据代码(以根据建筑预测名进行查找为示例):def run(): """ 每次查询前都要确保数据库存在 :return: NULL """ buildDatabase() df = pd.read_excel('buildings.xlsx', engine="openpyxl") # 读取建筑信息 for i in range(len(df)): value = [int(df.at[i, 'id']), df.at[i, 'name_pre'], df.at[i, 'name_cn'], df.at[i, 'category'], df.at[i, 'introduction'], df.at[i, 'location'], df.at[i, 'location_url']] insertValue(value) def gainContent_name_pre(name_pre): """ 获取数据库内容 :param name_pre: 要查询的建筑预测名 :return: contents,一个列表(要查询的建筑在数据库中的所有信息) """ run() # 插入数据 conn = sqlite3.connect('buildings.db') cur = conn.cursor() cur.execute(f''' SELECT * FROM buildings WHERE name_pre = '{name_pre}';''') contents = cur.fetchone() conn.close() return contents
4. 预告
好的,那么本篇文章就结束啦!今天主要讲解了如何用代码修改图片与文字以及数据库的使用,下一篇文章将会讲解浏览界面的优化(将下拉框和显示全部建筑名修改为滑动栏),要是有时间的话还会讲讲怎么对控件进行 QSS
美化。而“导航”操作应该会在下下篇文章进行介绍。
那我们下一篇文章见啦~
上一篇文章传送门:【PyQt5 实战项目1】武汉大学建筑知识系统–思路分享4(软件版本1.1.2介绍之新界面搭建与界面跳转)
下一篇文章传送门:【PyQt5 实战项目1】武汉大学建筑知识系统–思路分享6(优化浏览界面以及为我们的控件添加QSS样式)