PyQt实现一个简单的License系统(二)
本文接着上一篇继续讲解“PyQt实现一个简单的License系统”,主要包括:
3)如何用python创建一个GUI。
4)python如何调C DLL库。
5)ctypes中类型处理。
上一篇文章只是简单的将ui文件转换为py文件,并执行,生成了一个原始的GUI。本文将在这个基础上,运用python编码丰富这个GUI。
一、界面修饰
我们希望在界面生成的时候,自动获取系统时间,并将它转换为合适的格式,填充到GUI的控件:StartDate、DueDate和CurrentDate栏。实现方法如下:
1)导入时间库
在mainwindow.py的顶部相关位置添加一行代码
from datetime import date
from datetime import timedelta
2)获取系统时间,并显示到控件
在mainwindow.py文件的“setupUi”函数中添加如下代码:
#datetime today
currDate = date.today().strftime('%Y/%m/%d')
dueDate = (date.today() + timedelta(days=60)).strftime('%Y/%m/%d')
self.lineEdit_6.setText(currDate)
self.lineEdit_7.setText(dueDate)
self.lineEdit_8.setText(currDate)
注意:python的库非常丰富,它分为标准库和外部库。date就是一个标准库,可以通过python的在线文档查看。
https://docs.python.org/3/library/functions.html#bytearray
二、添加控件响应
我们需要分别添加“Preview”、“Encrypt”和“Decrypt”三个按钮的响应函数。
1)在mainwindow.py文件的“setupUi”函数中(尾部)添加如下代码:
self.retranslateUi(MainWindow)
self.btnPreview.clicked.connect(self.showplaintext)
self.btnEncrypt.clicked.connect(self.showencryptresult)
self.btnDecrypt.clicked.connect(self.showerecovertext)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
如上所示:第一行和最后一行是PyQt生成的代码,我们实际添加的是中间那三行。
另外需要注意的是:connect函数也可以在mainwindow.py文件外指定。
2)定义对应的函数
上一步绑定了三个控件响应函数(等同与Qt中的connect),这一步就是要具体定义这个三个响应函数。
在mainwindow.py文件的class Ui_MainWindow(object)中添加如下代码块:
def showplaintext(self):
print("preview")
def showencryptresult(self):
print("encrypt")
def showerecovertext(self):
print("decrypt")
这个就是python中类的成员函数的典型定义方式,需要带“self”参数,相当于C++中的this。
关于python的类和函数,也可以查看python文档库:
https://docs.python.org/3.6/tutorial/classes.html
三、GUI交互
上面只是建立了基本的程序框架,还没有具体细节。我们先来实现“showplainttext”函数:
def showplaintext(self):
print("preview")
listLabel = [self.label_1, self.label_2, self.label_3, self.label_4,\
self.label_5, self.label_6, self.label_7, self.label_8]
listLineEdit = [self.lineEdit_1,self.lineEdit_2,self.lineEdit_3,self.lineEdit_4,\
self.lineEdit_5,self.lineEdit_6,self.lineEdit_7,self.lineEdit_8]
head = "f0f0,"
strPlain = head
for n in range(8):
strPlain = strPlain + listLabel[n].text();
strPlain = strPlain + listLineEdit[n].text();
strPlain = strPlain + ',';
strPlain = strPlain + "0f0f";
self.plaintext.setPlainText(strPlain)
这段代码演示了python的列表(list)、for循环、字符串。
四、python调DLL
由于“LicenseSystem”要用到加密算法,而这个加密算法是一个外部的C++实现的DLL库,事实上,我对它进行了封装,导出两个函数:一个加密,一个解密。也因此,我们要用到“python调DLL”的技术。
“python调DLL”的几种方式中,我推荐用“ctypes”库。参考文档:
https://docs.python.org/3/library/ctypes.html
此外,也可以参考博客:https://zhuanlan.zhihu.com/p/20152309
http://blog.csdn.net/magictong/article/details/3075478
http://blog.csdn.net/magictong/article/details/3075478
有了“ctypes”,在python中加载DLL库还是比较简单的,难点在于“参数和返回值”的类型转换。在此,我折腾了好几个小时,才把我需要的类型完全正确转换完。目前还没有很深的理解,故不展开说了,请大家仔细研究我上面给出的“ctypes”文档和google。我在此仅贴出代码,以供参考:
def showencryptresult(self):
print("encrypt")
#lib = ctypes.WinDLL("EncryptorDll.dll")
lib = CDLL("EncryptorDll.dll")
plainText = bytes(self.plaintext.toPlainText(), encoding = "utf-8")
cPlainText = c_char_p(plainText)
fileName = self.lineEdit_3.text() + ".license"
cFileName = c_wchar_p(fileName)
bufKey = c_char_p(b"\x0A\x0B\x0C\x0D\x0E\x0F\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10")
bufIV = c_char_p(b"\x0F\x0E\x0D\x0C\x0B\x0A\x10\x09\x08\x07\x06\x05\x04\x03\x02\x01")
#ret = lib.EncryptString2File(byref(cPlainText), byref(cFileName), byref(bufKey), byref(bufIV), 16)
ret = lib.EncryptString2File(cPlainText, cFileName, bufKey, bufIV, 16)
if 1 == ret:
self.ciphertext.setPlainText("Encrypt succeed!")
else:
self.ciphertext.setPlainText("Encrypt failed!")
def showerecovertext(self):
print("decrypt")
lib = CDLL("EncryptorDll.dll")
fileName = self.lineEdit_3.text() + ".license"
filesize = os.path.getsize(fileName)
print(filesize)
recover = create_string_buffer(b'\0'*filesize)
print(recover)
cRecover = c_char_p(recover.value)
cFileName = c_wchar_p(fileName)
bufKey = c_char_p(b"\x0A\x0B\x0C\x0D\x0E\x0F\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10")
bufIV = c_char_p(b"\x0F\x0E\x0D\x0C\x0B\x0A\x10\x09\x08\x07\x06\x05\x04\x03\x02\x01")
ret = lib.DecryptFile2String(cFileName, cRecover, bufKey, bufIV, 16)
if 1 == ret:
self.recovertext.setPlainText(str(cRecover.value, encoding = "utf-8"))
else:
self.recovertext.setPlainText("Encrypt failed!")
注:CDLL对应“cdecl”,而WinDLL对应“stdcall”
被调的C函数原型为:
bool(*pfnEncrypt)(const char *, const wchar_t *, unsigned char *, unsigned char *, int) =
(bool(*)(const char *, const wchar_t *, unsigned char *, unsigned char *, int))m_library.resolve("EncryptString2File");
bool(*pfnDecrypt)(const wchar_t *, char *, unsigned char *, unsigned char *, int) =
(bool(*)(const wchar_t *, char *, unsigned char *, unsigned char *, int))m_library.resolve("DecryptFile2String");
上面的代码共涉及到如下几种类型转换:
1)QString或string转char数组(bytes),然后转char指针
plainText = bytes(self.plaintext.toPlainText(), encoding = "utf-8")
cPlainText = c_char_p(plainText)
2)QString或string转wchar_t指针
fileName = self.lineEdit_3.text() + ".license"
cFileName = c_wchar_p(fileName)
比较上面两条可知:python默认的str是Unicode编码,即宽字符编码(wchar_t)
3)以0结尾的字符串常量
bufKey = c_char_p(b"\x0A\x0B\x0C\x0D\x0E\x0F\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10")
注意,括号里的“b”是将字符串指定为char字符(字节字符),而默认是Unicode字符。
也有一种说法:
这里的“b”指的是“binary”(二进制),也就是说,python中的bytes object是以二进制的形式保存的。而str是以“文本”形式保存的,即“Unicode”。
4)返回值bool
if 1 == ret:
self.ciphertext.setPlainText("Encrypt succeed!")
else:
self.ciphertext.setPlainText("Encrypt failed!")
五、运行
添加完上述代码,再将加密库“EncryptorDll.dll”文件与“mainwindow.py”文件放在同一个目录下,就已经是一个完整的“LicenseSystem”软件了。
同前一篇文章,在cmd中,用python执行“mainwindow.py”,即可启动该软件。
六、软件架构
尽管这个已经是一个完整的软件了,但是从架构上来说,它并不是很好。我们是直接在“mainwindow.py”文件中实现这个功能的,如果后续需要修改界面,即修改“mainwindow.ui”文件,我们又需要重新编译生成新的“mainwindow.py”文件,然后再合并“mainwindow.py”文件。这样显得很麻烦!
更好的办法是:保持“mainwindow.py”文件不变,我们将功能在另外一个文件中实现。
这里我新建了一个“start.py”文件
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from mainwindow import Ui_MainWindow
class LicenseGui(Ui_MainWindow):
def __init__(self, mainwindow):
Ui_MainWindow.__init__(self)
self.setupUi(mainwindow)
# Connect "add" button with a custom function (addInputTextToListbox)
#self.addBtn.clicked.connect(self.addInputTextToListbox)
'''
def addInputTextToListbox(self):
txt = self.myTextInput.text()
self.listWidget.addItem(txt)
'''
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
mainwindow = QtWidgets.QMainWindow()
prog = LicenseGui(mainwindow)
mainwindow.show()
sys.exit(app.exec_())
我们可以将其他功能在这个文件中实现,而保存“mainwindow.py”的原生态。然后,在程序执行的时候,从这个文件启动。
从上面的程序可以看出:类LicenseGui实际上是对“mainwindow.py”中原生态的类“Ui_MainWindow”的封装。
本文地址:https://blog.csdn.net/Sagittarius_Warrior/article/details/72831039
上一篇: LM386喇叭驱动电路
下一篇: 【Golang】1.初次接触Go和初次使用Goland编写运行第一个Helloworld程序以及Main file has non-main package or doesn‘t报错
推荐阅读
-
自己制作一个简单的操作系统二[CherryOS]
-
基于Python编写一个计算器程序,实现简单的加减乘除和取余二元运算
-
一个简单的自动发送邮件系统(二)
-
使用MongoDB和JSP实现一个简单的购物车系统实例
-
假设客车的座位数是9行4列,使用二维数组在控制台应用程序中实现简单的客车售票系统。
-
用VUE实现一个简单的学生信息管理系统
-
android硬编码h264数据,并使用rtp推送数据流,实现一个简单的直播-MediaCodec(二)
-
PyQt实现一个简单的License系统(二)
-
荐 使用Python PyQt5实现一个简单的图像识别软件
-
Python实现一个简单的毕业生信息管理系统的示例代码