嗯嗯,最近几周荒废了手头的一切事情,搞出来一个这个。可把胖虎累成大雄了(~__~)~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代码
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", "示例"))
但是这只是设计界面,并没有运行的语句
我们在代码的末尾添上以下初始化类的代码
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
ex = Ui_MainWindow() #创建对象
w = QtWidgets.QMainWindow()
ex.setupUi(w) #调用setupUi进行初始化
w.show() #显示窗口
sys.exit(app.exec_())
然后运行就可以了。
PyQt5
界面类的继承
我们分别设计好不同操作的界面后,如何整合在主程序中呢?
很简单,在主程序main.py中创建多个类,分别继承之前的界面的类就好啦。
比如
from typecho.gui.mainwindow import Ui_MainWindow #导入设计好的主界面的类
class MainWindow(QMainWindow, Ui_MainWindow): #新建的类继承QMainWindow和自定义的类Ui_MainWindow
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent) #super是用来解决多重继承问题的
self.setupUi(self) #父类Ui_MainWindow的函数,用于初始化界面
def main():
app = QApplication(sys.argv)
mainwindow = MainWindow()
mainwindow.show() 内置方法,用于显示界面
sys.exit(app.exec_())
if __name__=="__main__":
main()
信号与槽函数
基础
以打开缓存目录的相关代码为例
#打开缓存目录
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本质上就是一个表达式,通过它可以把函数的参数隐藏在表达式内部,从而达到传递参数的作用。
以下载并打开文章的相关代码为例
#快速更新独立页面
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表达式换成函数,可以表示为:
def func():
if bool_1:
A(x,y,z)
elif bool_2:
B(x,y,z)
elss:
C(x,y,z)
自定义信号
有时候完成一定的任务后需要做一些操作,但是由于跨文件等原因,没法直接调用相关函数,因为无法判断何时完成操作。这时可以自定义信号,任务完成后发送信号,主程序中接受信号并调用槽函数。
#create_post.py
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):
#doing somgthing
self.list_update_passages_signal.emit() #emit发射信号
#main.py
#创建文章
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
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) #将StringListModel加载到QListView
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
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)