嗯嗯,最近几周荒废了手头的一切事情,搞出来一个这个。可把胖虎累成大雄了(~__~)~zZ
先放程序预览图:
好久没写博文辣,今天咱就来撸一篇,记录一下这个项目的心得。
说起来,这算是我写的第三个Typecho博客管理的程序了。
- 第一个程序(typecho_post)算是入门,功能仅限上传和创建分类。甚至当时没分析好token(因为当时没接触过PHP,实在读不懂源代码),所以被迫将token固定住。显然,这样很容易受到攻击。
- 第二个程序(typecho_desktop_cmd)于我而言,是MySQL学习的一大助推力。没错,我,使用pymysql,近似地写了个Typecho的Python后端。后悔死我了。因为初学数据库,各方面处理的都不到位,考虑的并不全面。因为是直接对数据库操作,所以可能插入一些奇奇怪怪的东西,这些东西我都不会处理。而且最最重要的是,我的处理机制和官方的处理机制可能不可能完全相同,一旦有一处不同(比如说少插入了一列数据),后期在web段处理的时候就可能导致错误。轻则显示异常,重则直接报数据库错误。
- 第三个程序(typecho_desktop)就是这个啦。为防止自己写入数据出错,所以,所有涉及数据库改动的操作均模拟web端的操作发包,只有部分读取数据的操作使用pymysql获取。最最重要的是,这个是可视化的程序啦。
Qt creater
Qt creater可以设计界面,得到.ui文件,然后通过PyQt5-tools中的pyuic5可以将其转化为.py代码。大大节省设计时间。
文件->新建文件或项目->Qt 设计师界面类(英文版为Qt designer)->choose
Dialog代表对话框界面,Main Window是主界面,可以根据实际选择。Widget是基类。
主界面并非一个项目只能有一个,不要走入误区。
设置基本信息,不懂的话默认即可。其实只需要改一下类名就好,对我们之后的工作有影响的只有类名。
靠左侧的一列选项是各种设计元素,点击后拖动到设计框中即可。
然后就可以开始设计了。这里不过多展开,毕竟网上一堆教程,说话又好听。
设计好之后保存,得到*.ui文件。
我们shift+鼠标右键
打开PowerShell,键入
pyuic5 -o xxx.py xxx.ui
即可转换成相应的python代码
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
| from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(341, 157) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.label = QtWidgets.QLabel(self.centralwidget) self.label.setGeometry(QtCore.QRect(30, 50, 54, 12)) self.label.setObjectName("label") self.lineEdit = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit.setGeometry(QtCore.QRect(90, 50, 221, 20)) self.lineEdit.setObjectName("lineEdit") MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 341, 23)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.label.setText(_translate("MainWindow", "示例"))
|
但是这只是设计界面,并没有运行的语句
我们在代码的末尾添上以下初始化类的代码
1 2 3 4 5 6 7 8
| if __name__ == '__main__': import sys app = QtWidgets.QApplication(sys.argv) ex = Ui_MainWindow() w = QtWidgets.QMainWindow() ex.setupUi(w) w.show() sys.exit(app.exec_())
|
然后运行就可以了。
PyQt5
界面类的继承
我们分别设计好不同操作的界面后,如何整合在主程序中呢?
很简单,在主程序main.py中创建多个类,分别继承之前的界面的类就好啦。
比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| from typecho.gui.mainwindow import Ui_MainWindow
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) def main(): app = QApplication(sys.argv) mainwindow = MainWindow() mainwindow.show() 内置方法,用于显示界面 sys.exit(app.exec_())
if __name__=="__main__": main()
|
信号与槽函数
基础
以打开缓存目录的相关代码为例
1 2 3 4 5
| def open_dir(): operation = Operation() return operation.open_dir() mainwindow.pushButton_open_dir.clicked.connect(open_dir)
|
open_dir()是槽函数,clicked是内置的信号。connect的作用是将此信号连接到槽函数。
当mainwindow(对象)的pushButton_open_dir这个按钮被点击时,会发送一个clicked信号,从而触发槽函数。
通过lambda传递参数
connect的参数是一个表达式,无法传递参数。可以使用lambda,因为lambda本质上就是一个表达式,通过它可以把函数的参数隐藏在表达式内部,从而达到传递参数的作用。
以下载并打开文章的相关代码为例
1 2 3 4 5 6 7 8 9 10 11
| mainwindow.pushButton_update_page_fast.clicked.connect( lambda: QMessageBox.warning(None, '提示', '请先进入独立页面界面!', QMessageBox.Ok, QMessageBox.Ok) if mainwindow.list_info != 'pages' else QMessageBox.warning(None, '提示', '请先选中要快速更新的独立页面!', QMessageBox.Ok, QMessageBox.Ok) if mainwindow.listView_passages.currentIndex().row() == -1 else update_page_fast())
def update_page_fast(): operation = Operation() return operation.update_page_fast(mainwindow.passages_detail_list[mainwindow.listView_passages.currentIndex().row()]['cid'])
|
表达式有些长,我简写一下:
AAA.BBB.clicked.connect(lambda: A(x,y,z) if bool_1 else B(x,y,z) if bool_2 else C(x,y,z))
其中的lambda表达式换成函数,可以表示为:
1 2 3 4 5 6 7
| def func(): if bool_1: A(x,y,z) elif bool_2: B(x,y,z) elss: C(x,y,z)
|
自定义信号
有时候完成一定的任务后需要做一些操作,但是由于跨文件等原因,没法直接调用相关函数,因为无法判断何时完成操作。这时可以自定义信号,任务完成后发送信号,主程序中接受信号并调用槽函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_create_post(object): def setupUi(self, create_post): pass list_update_passages_signal = QtCore.pyqtSignal() pass def start(self): self.list_update_passages_signal.emit()
|
1 2 3 4 5 6 7 8 9 10 11
|
create_post = Create_post() mainwindow.pushButton_create_post.clicked.connect( lambda: create_post.open(mainwindow.metas_detail_list, mainwindow.listView_metas.currentIndex().row())) create_post.list_update_passages_signal.connect( lambda: create_post.pass_value(mainwindow.metas_detail_list)) create_post.list_update_passages_signal.connect( lambda: mainwindow.list_posts(0) if mainwindow.listView_metas.currentIndex().row() == -1 or mainwindow.listView_metas.currentIndex().row() == 0 else mainwindow.list_posts(mainwindow.metas_detail_list[mainwindow.listView_metas.currentIndex().row()]['mid']))
|
总体就三条语句。
创建自定义信号,发射信号,连接信号与槽函数。
控件
下面记录一下我用到的控件(QtWidgets)以及了解到的方法函数
文本框QLineEdit
text()
获取文本
setText(str)
设置文本
setInputMask('0000-00-00 00:00;_')
设置文本框掩码(具体继续百度)
文本段QTextEdit
toPlainText()
获取文本
下拉菜单QComboBox
currentIndex()
获取当前所在行索引(从0开始, -1为未选中)
setCurrentIndex(int)
设置索引
addItems(list)
添加选项
clear()
清空选项
复选框QCheckBox
setText()
设置复选框右侧文字
setChecked(bool)
设置选中/未选中
isChecked()
是否选中
列表QListView
QListView没有数据库,所以需要配合QtCore.QStringListModel
1 2 3 4 5 6 7 8 9
| self.listView_metas = QtWidgets.QListView(self.tab) self.listView_metas.setGeometry(QtCore.QRect(-1, -1, 241, 523)) self.listView_metas.setObjectName("listView_metas")
self.slm = QtCore.QStringListModel(); self.slm.setStringList(self.metas_name_list) self.listView_metas.setModel(self.slm) self.listView_metas.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self.listView_metas.clicked.connect(self.clicked_meta)
|
currentIndex() #获取当前索引(不是int哦,而是对象)
currentIndex().row() #当前所在行
打包
如果需要重新编译并打包成exe的话,可以参考以下内容
环境win10 x86(虚拟机), python 3.7 x86
1 2 3 4
| pip install PyQt5==5.11.3 pip install PyQt5-tools==5.9.2.1.3 pip install PyQt5-sip==4.19.19 pip install pyinstaller
|
然后在cmd(或PowerShell)中进入项目根目录,运行
pyinstaller -i typecho.ico -F -w main.py
成功后可在dist文件夹内看到exe文件
相关打包具体事宜记录在:PyQt5真香啊
其他
导入数据库问题
从linux导出后无法导入至windows
程序写好后,重新上传了所有的文章,并在linuxd导入了blog.sql,但是导入windows时失败
因为linux默认utf8,windows默认gbk。可使用下面的命令导入。
mysql -uroot -p --default-character-set=utf8 blog3 < d:/桌面/blog.sql
注意:
d:/桌面/blog.sql
或d:\\桌面\\blog.sql
都行,千万不能d:\桌面\blog.sql
,因为转义(虽然我仍然没搞明白命令行里又不是mysql语句,为什么会转义)
--default-character-set=utf8
指定utf8
靠该命令解决了typecho博客数据库在本地导入失败的问题。(之前导入一直用source)