使用pyqt5来开发图形化工具

前言

这里介绍一些PyQt的基本使用,以及一些常用的组件,以及如何使用Qt Designer设计图形化界面。

一篇文章教会小白写图形化界面工具。

1.PyQt介绍与安装

PyQt的开发者是英国的“Riverbank Computing”公司。它提供了GPL(简单的说,以GPL协议发布到网上的素材,你可以使用,也可以更改,但是经过你更改然后再次发布的素材必须也遵守GPL协议,主要要求是必须开源,而且不能删减原作者的声明信息等)与商业协议两种授权方式,因此它可以免费地用于自由软件的开发。
PyQt是Python语言的GUI(Graphical User Interface,简称 GUI,又称图形用户接口)编程解决方案之一
可以用来代替Python内置的Tkinter。其它替代者还有PyGTK、wxPython等,与Qt一样,PyQt是一个自由软件

安装PyQt5

  1. pip install pyqt5 -i https://mirrors.aliyun.com/pypi/simple/

2.PyQt5使用示例

  1. import sys

  2. from PyQt5.QtWidgets import QApplication, QWidget

  3. if __name__ == '__main__':

  4. app = QApplication(sys.argv)

  5. w = QWidget()

  6. # 设置窗口标题

  7. w.setWindowTitle("第一个PyQt")

  8. # 展示窗口

  9. w.show()

  10. # 程序进行循环等待状态

  11. app.exec()

图片

3.PyQt5组件以及布局

这里的组件和布局比较简单,下面链接介绍比较全面

https://doc.itprojects.cn/0001.zhishi/python.0008.pyqt5rumen/index.html#/README

4.Qt Designer介绍

纯靠代码来编写界面,效率属实是有点底,介绍一个辅助设计图形化的软件 QT Designer。

可以直接拖动组件设计ui界面。

图片

保存后的文件为ui文件

图片

若要加载ui文件,则需要导入 uic 模块 , 它位于PyQt5 中

 
  1. import sys

  2. from PyQt5.QtWidgets import QApplication

  3. from PyQt5 import uic

  4. if __name__ == '__main__':

  5. app = QApplication(sys.argv)

  6. ui = uic.loadUi("./untitled.ui")

  7. # 展示窗口

  8. ui.show()

  9. app.exec()

不过使用.ui去调用就无法和py文件一起打包成exe,这里可以使用pyuic5将ui文件转换成python文件

在cmd中执行

pyuic5 -o .py .ui

pyuic5 -o tishi_ui.py tishi.ui

图片

调用py文件

 
  1. from PyQt5.QtCore import Qt

  2. from .tishi_ui import Ui_Form

  3. from PyQt5.QtWidgets import QWidget

  4. class TiShi(QWidget,Ui_Form):

  5. def __init__(self):

  6. super(TiShi, self).__init__()

  7. self.setupUi(self) # 使用 sjui.Ui_Form 类中的方法初始化 UI

  8. self.setWindowFlags(Qt.WindowCloseButtonHint)

5.Pyqt5项目 Xray-Gui

这项目是通过pyqt5实现的一个Xray的图形界面,方便用户使用。

项目地址: https://github.com/buluorifu/Xray-Gui

构思

图片

文件夹结构

图片

效果图

图片

使用Qt Designer设计界面

1. 打开Qt Designer,并设计好ui界面

图片


 

图片


 

图片


 

图片


 

图片


 

图片

图片

保存好会生成.ui文件

图片

2. 将ui文件转化为py文件

  1. pyuic5 -o ***.py ***.ui

图片

3.调用py文件打开界面

 
  1. import sys

  2. from PyQt5.QtGui import QIcon

  3. from configuration.box1 import BOX1

  4. from configuration.box2 import BOX2

  5. from configuration.box3 import BOX3

  6. from configuration.homepage import Ui_Form

  7. from PyQt5.QtWidgets import QWidget, QApplication, QStackedLayout

  8. class MyWindow(QWidget,Ui_Form):

  9. def __init__(self):

  10. super(MyWindow, self).__init__()

  11. self.setupUi(self) # 使用 sjui.Ui_Form 类中的方法初始化 UI

  12. self.init_ui()

  13. self.sub_window = None # 存储子窗口对象的属性

  14. def init_ui(self):

  15. self.qsl = QStackedLayout(self.groupBox_2)

  16. self.box1 = BOX1()

  17. self.box2 = BOX2()

  18. self.box3 = BOX3()

  19. self.qsl.addWidget(self.box1)

  20. self.qsl.addWidget(self.box2)

  21. self.qsl.addWidget(self.box3)

  22. # 给按钮添加事件(即点击后要调用的函数)

  23. self.pushButton.clicked.connect(self.btn_press1_clicked)

  24. self.pushButton_2.clicked.connect(self.btn_press2_clicked)

  25. self.pushButton_3.clicked.connect(self.btn_press3_clicked)

  26. # 设置按钮1的初始样式

  27. self.pushButton.setStyleSheet("background-color: #CAE1FF;")

  28. def btn_press1_clicked(self):

  29. self.qsl.setCurrentIndex(0)

  30. self.pushButton.setStyleSheet("background-color: #CAE1FF;")

  31. self.pushButton_2.setStyleSheet("") # 恢复按钮2的默认样式

  32. self.pushButton_3.setStyleSheet("") # 恢复按钮3的默认样式

  33. def btn_press2_clicked(self):

  34. self.qsl.setCurrentIndex(1)

  35. self.pushButton.setStyleSheet("") # 恢复按钮1的默认样式

  36. self.pushButton_2.setStyleSheet("background-color: #CAE1FF;")

  37. self.pushButton_3.setStyleSheet("") # 恢复按钮3的默认样式

  38. def btn_press3_clicked(self):

  39. self.qsl.setCurrentIndex(2)

  40. self.pushButton.setStyleSheet("") # 恢复按钮1的默认样式

  41. self.pushButton_2.setStyleSheet("") # 恢复按钮2的默认样式

  42. self.pushButton_3.setStyleSheet("background-color: #CAE1FF;")

  43. def closeEvent(self, event):

  44. # 关闭其他窗口的代码

  45. for widget in QApplication.topLevelWidgets():

  46. if isinstance(widget, QWidget) and widget != self:

  47. widget.close()

  48. event.accept()

  49. if __name__ == '__main__':

  50. app = QApplication(sys.argv)

  51. w = MyWindow()

  52. icon = QIcon('./img/扫描.png')#添加图标

  53. w.setWindowIcon(icon)

  54. w.show()

  55. sys.exit(app.exec_())

我们在box1、box2、box3各把ui里面的代码导入即可,以box1举例

  1. from PyQt5.QtWidgets import QWidget

  2. from .box1_ui import Ui_Form

  3. class BOX1(QWidget,Ui_Form):

  4. def __init__(self):

  5. super(QWidget, self).__init__()

  6. self.setupUi(self) # 使用 sjui.Ui_Form 类中的方法初始化 UI

  7. self.init_ui()

4.功能调用介绍

self.init_ui()创建对象时自动调用

init_ui()里面主要为按钮连接的方法

图片

读取文件功能,获取文件路径

 
  1. fileName, fileType = QtWidgets.QFileDialog.getOpenFileName(None, "选取文件", os.getcwd(), "All Files(*.exe);")

  2. self.lineEdit.setText(fileName)

图片

创建子进程,这里不创建子进程会把整个程序阻塞

 
  1. # 创建子进程

  2. def process_creation(self):

  3. self.process = QProcess()

  4. self.process.setProcessChannelMode(QProcess.MergedChannels)

  5. self.process.readyReadStandardOutput.connect(self.handle_output)

  6. self.process.finished.connect(self.handle_finished)

  7. self.process.start(self.lineEdit.text())

  8. # 命令输出

  9. def handle_output(self):

  10. try:

  11. data = self.process.readAll().data().decode('utf-8').rstrip()

  12. # 将命令行输出至文本框

  13. self.textEdit.append(data)

  14. except:

  15. data = self.process.readAll().data().decode('latin-1').rstrip()

  16. # 将命令行输出至文本框

  17. self.textEdit.append(data)

这里可以对输出的文本进行优化一下颜色,不然是全黑色字体和xray原本输出的颜色不符

通过正则匹配,对不同字段进行不同颜色的输出

 
  1. # 命令输出

  2. def handle_output(self):

  3. try:

  4. data = self.process.readAll().data().decode('utf-8').strip()

  5. lines = data.split('\n')

  6. for line in lines:

  7. if "[INFO]" in line:

  8. line = line.replace("[INFO]", f'<span style="color: blue;">[INFO]</span>')

  9. elif "[Vuln: dirscan]" in line:

  10. line = line.replace("[Vuln: dirscan]", f'<span style="color: red;">[Vuln: dirscan]</span>')

  11. elif line.startswith('\t'):

  12. line = f'<span style="color: purple;">&nbsp;&nbsp;&nbsp;&nbsp;{line}</span>'

  13. elif re.match(r'^[\u4e00-\u9fff]', line):

  14. line = f'<span style="color: red;">{line}</span>'

  15. elif "requestSent" in line:

  16. line = f'<span style="color: #FFBF00;">{line}</span>'

  17. elif "All pending" in line:

  18. line = f'<span style="color: #00FF7F;">{line}</span>'

  19. self.textEdit.append(line)

  20. except:

  21. data = self.process.readAll().data().decode('latin-1').strip()

  22. lines = data.split('\n')

  23. for line in lines:

  24. if "[INFO]" in line:

  25. line = line.replace("[INFO]", f'<span style="color: blue;">[INFO]</span>')

  26. elif "[Vuln: dirscan]" in line:

  27. line = line.replace("[Vuln: dirscan]", f'<span style="color: red;">[Vuln: dirscan]</span>')

  28. elif line.startswith('\t'):

  29. line = f'<span style="color: purple;">&nbsp;&nbsp;&nbsp;&nbsp;{line}</span>'

  30. elif re.match(r'^[\u4e00-\u9fff]', line):

  31. line = f'<span style="color: red;">{line}</span>'

  32. elif "requestSent" in line:

  33. line = f'<span style="color: #FFBF00;">{line}</span>'

  34. elif "All pending" in line:

  35. line = f'<span style="color: #00FF7F;">{line}</span>'

  36. self.textEdit.append(line)

图片

修改配置文件主要在于修改config.yaml文件,这里用import ruamel.yaml这个库。

 
  1. # 确认修改基础配置

  2. def basics_save(self):

  3. if os.path.exists('file_address.yaml'):

  4. with open('config.yaml', 'r', encoding='utf-8') as file:

  5. yaml = ruamel.yaml.YAML()

  6. config = yaml.load(file)

  7. config['parallel'] = int(self.spinBox.text())

  8. config['http']['dial_timeout'] = int(self.spinBox_2.text())

  9. config['http']['max_redirect'] = int(self.spinBox_3.text())

  10. config['http']['max_qps'] = int(self.spinBox_4.text())

  11. with open('config.yaml', 'w', encoding='utf-8') as file:

  12. yaml.dump(config, file)

  13. self.open_tishiwindow(None, None)

  14. else:

  15. text = '未配置xray文件'

  16. img = './img/失败.png'

  17. self.open_tishiwindow(text, img)

然后通过子进程去输出命令行,并调用handle_output方法输出到文本

这里通过获取界面的选项来拼接命令行

然后config界面和rad界面同理

 
  1. def initiative_scan(self):

  2. if os.path.exists('file_address.yaml'):

  3. self.textEdit.clear()

  4. self.process_kill()

  5. if not hasattr(self, 'is_first_click'):

  6. if self.lineEdit_2.text() == "" or self.lineEdit_2.text() == "默认则随机命名":

  7. self.dict['name'] = str(int(time.time()))

  8. else:

  9. self.dict['name'] = self.lineEdit_2.text()

  10. # 第一次按下按钮

  11. self.is_first_click = True

  12. self.process_kill()

  13. with open('file_address.yaml', 'r', encoding='utf-8') as file:

  14. yaml = ruamel.yaml.YAML()

  15. config = yaml.load(file)

  16. self.args = config['xray_address']

  17. if self.radioButton_5.isChecked():

  18. self.args = self.args + ' --log-level debug'

  19. if self.radioButton_6.isChecked():

  20. self.args = self.args + ' --log-level info'

  21. if self.radioButton_7.isChecked():

  22. self.args = self.args + ' --log-level warn'

  23. if self.radioButton_8.isChecked():

  24. self.args = self.args + ' --log-level error'

  25. if self.radioButton_9.isChecked():

  26. self.args = self.args + ' --log-level fatal'

  27. self.args = self.args + ' webscan'

  28. if self.radioButton_11.isChecked():

  29. self.args = self.args + ' --level medium'

  30. if self.radioButton_12.isChecked():

  31. self.args = self.args + ' --level high'

  32. if self.radioButton_13.isChecked():

  33. self.args = self.args + ' --level critical'

  34. if self.dict['url']:

  35. self.args = self.args + ' --url ' + self.dict['url']

  36. if self.dict['request']:

  37. self.args = self.args + ' --raw-request ' + self.dict['request']

  38. if self.dict['url_list']:

  39. self.args = self.args + ' --url-file ' + self.dict['url_list']

  40. if self.radioButton.isChecked():

  41. self.args = self.args + ' --html-output ' + self.dict['name'] + '.html'

  42. self.dict['name_all'] = os.path.dirname(config['xray_address']) + '/' + self.dict['name'] + '.html'

  43. if self.radioButton_2.isChecked():

  44. self.args = self.args + ' --json-output ' + self.dict['name'] + '.txt'

  45. self.dict['name_all'] = os.path.dirname(config['xray_address']) + '/' + self.dict['name'] + '.txt'

  46. self.pushButton_3.setText("关闭主动扫描")

  47. self.process_creation()

  48. self.process.start(self.args)

  49. else:

  50. # 第二次按下按钮

  51. del self.is_first_click # 删除标记

  52. self.pushButton_3.setText("开启主动扫描")

  53. self.textEdit.clear()

  54. self.process_kill()

  55. else:

  56. text = '未配置xray文件'

  57. img = './img/失败.png'

  58. self.open_tishiwindow(text, img)

被动代理效果,红色即是扫出的漏洞

图片

打开输出文件

 
  1. # 查看扫描结果

  2. def file_look(self):

  3. try:

  4. if self.dict['name_all']:

  5. os.startfile(self.dict['name_all'])

  6. else:

  7. text = '没有输出文件'

  8. img = './img/失败.png'

  9. self.open_tishiwindow(text, img)

  10. except:

  11. text = '没有输出文件'

  12. img = './img/失败.png'

  13. self.open_tishiwindow(text, img)

图片

加解密界面主要通过self.textEdit.textChanged.connect(self.encrypt_txt)去对输入的文本进行处理

这里可以创建一个线程,去执行加密操作,避免阻塞界面

 
  1. def start_encrypt_txt(self):

  2. t = threading.Thread(target=self.encrypt_txt)

  3. t.daemon = True

  4. t.start()

md5的界面主要通过字典匹配,如果匹配不到,就返回无法解密

 
  1. found_match = False # 标记是否找到匹配的解密结果

  2. with open('./dict/top1w-md5.txt', 'r', encoding='utf-8') as fp:

  3. for line in fp:

  4. if str_txt == line.strip():

  5. # 如果找到了匹配的MD5哈希值,我们可以从原始文件中获取相应的单词

  6. with open("./dict/top1w.txt", 'r', encoding='utf-8') as wp:

  7. for word_line in wp:

  8. words = word_line.split()

  9. for word in words:

  10. if hashlib.md5(word.encode('utf-8')).hexdigest() == str_txt:

  11. found_match = True

  12. self.textEdit_6.setText(word)

  13. break

  14. if found_match:

  15. break

  16. if not found_match:

  17. self.textEdit_6.setText('无法解密')

5.把python打包成exe文件
  1. 安装pyinstaller

首先我们要先安装Pyinstaller,直接在cmd使用pip命令

 
  1. pip install pyinstaller

2、执行命令,从程序开始的地方打包,生成exe文件

末尾出现Building EXE from EXE-00.toc completed successfully.代表打包成功

打包后在当前路径dist目录下面生成exe文件

 
  1. Pyinstaller -F -w -i 扫描.ico ui.py

图片


 

图片


 

图片

图片

一个图形化工具就写完了

 

免费获取网络安全优质学习资料与干货教程

申明:本账号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值