PyQt实现一个简单的License系统(二)

原创 2017年06月01日 15:41:15

    本文接着上一篇继续讲解“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

                                        /magictong/article/details/3075478

                                        /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”的封装。


PyQt实现一个简单的License系统(一)

1)PyQt、WinPython的安装、配置。 2)如何用PyQt编译QDesigner生成的*.ui文件。 3)如何用python创建一个GUI。 4)python如何调C DLL库。 5)ctyp...
  • Sagittarius_Warrior
  • Sagittarius_Warrior
  • 2017年06月01日 14:35
  • 426

不到100行代码实现一个简单的推荐系统

一个好的推荐系统推荐的精度必然很高,能够真的发现用户的潜在需求或喜好,提高购物网詀的销量,让视频网站发现用户喜欢的收费电影… 可是要实现一个高精度的推荐系统不是那么容易的,netflix曾经悬赏高额奖...
  • zkl99999
  • zkl99999
  • 2015年06月12日 21:32
  • 1069

Python实现系统桌面时钟

用Python + PyQT写的一个系统桌面时钟,刚学习Python,写的比较简陋,但是基本的功能还可以。 功能: ①窗体在应用程序最上层,不用但是打开其他应用后看不到时间 ②左键双击全屏,可以做小屏...
  • gatieme
  • gatieme
  • 2013年12月29日 21:07
  • 22755

【系统设计】B/S 简单商业系统License控制解决方案之一

B/S系统简单License控制 背景:            目前主流商用的系统软件都存在License控制的功能,最主要的目的是为了满足开发商的盈利,作为一种试用、销售的策略。其二 就是为了...
  • sweetyone
  • sweetyone
  • 2014年09月11日 01:19
  • 1435

一个简单的推荐系统

咱以电影电视的推荐系统为例,一步一步的来实现一个简单的推荐系统吧, 由于比较简单,整个推荐系统源码不到100行,大概70-80行吧,应该很容易掌握。 为了快速开发原型,咱采用Python代码来演示 1...
  • jiyang_1
  • jiyang_1
  • 2015年12月04日 17:03
  • 1283

一个简单的粒子系统

一个简单的粒子系统
  • funte
  • funte
  • 2013年12月02日 16:02
  • 1551

一个简单的论坛系统

一个简单的论坛系统  1:包含下列信息:    2:每天论坛访问量300万左右,更新帖子10万左右。  请给出数据库表结构设计,并结合范式简要说明设计思路。  一. 发帖主题和回...
  • CareChere
  • CareChere
  • 2016年04月27日 11:00
  • 1147

License系统设计(二)

本文主要讲解License Generator的实现,总体设计请参考前一篇文章。 一、导出导入库     License Generator是采用QT实现的,它需要导入前面的加解密DLL。导入库的设计...
  • Sagittarius_Warrior
  • Sagittarius_Warrior
  • 2016年12月07日 10:36
  • 796

简单的员工管理系统

1.问题描述 设计一个计算机程序,能够实现简单的员工管理功能。 2.实习要求 (1)每个员工的信息包括:编号、姓名、性别、出生年月、学历、职务、电话、住址等。 (2)系统的功能包括:    ...
  • dancheng1
  • dancheng1
  • 2017年07月11日 22:59
  • 542

用python写一个简单的推荐系统

前言 在上篇文章豆瓣电影,电视剧DM实战中提及到,我和室友们产生了剧荒,萌生出要做一个个人用的推荐系统,解决剧荒的问题,经过一轮的死缠烂打,这个个人推荐系统终于成型了。 今天来分享一下心得...
  • GarfieldEr007
  • GarfieldEr007
  • 2016年05月22日 17:47
  • 3020
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:PyQt实现一个简单的License系统(二)
举报原因:
原因补充:

(最多只允许输入30个字)