前言
这里介绍一些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
-
pip install pyqt5 -i https://mirrors.aliyun.com/pypi/simple/
2.PyQt5使用示例
-
import sys
-
from PyQt5.QtWidgets import QApplication, QWidget
-
if __name__ == '__main__':
-
app = QApplication(sys.argv)
-
w = QWidget()
-
# 设置窗口标题
-
w.setWindowTitle("第一个PyQt")
-
# 展示窗口
-
w.show()
-
# 程序进行循环等待状态
-
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 中
-
import sys
-
from PyQt5.QtWidgets import QApplication
-
from PyQt5 import uic
-
if __name__ == '__main__':
-
app = QApplication(sys.argv)
-
ui = uic.loadUi("./untitled.ui")
-
# 展示窗口
-
ui.show()
-
app.exec()
不过使用.ui去调用就无法和py文件一起打包成exe,这里可以使用pyuic5将ui文件转换成python文件
在cmd中执行
pyuic5 -o .py .ui
pyuic5 -o tishi_ui.py tishi.ui
调用py文件
-
from PyQt5.QtCore import Qt
-
from .tishi_ui import Ui_Form
-
from PyQt5.QtWidgets import QWidget
-
class TiShi(QWidget,Ui_Form):
-
def __init__(self):
-
super(TiShi, self).__init__()
-
self.setupUi(self) # 使用 sjui.Ui_Form 类中的方法初始化 UI
-
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文件
-
pyuic5 -o ***.py ***.ui
3.调用py文件打开界面
-
import sys
-
from PyQt5.QtGui import QIcon
-
from configuration.box1 import BOX1
-
from configuration.box2 import BOX2
-
from configuration.box3 import BOX3
-
from configuration.homepage import Ui_Form
-
from PyQt5.QtWidgets import QWidget, QApplication, QStackedLayout
-
class MyWindow(QWidget,Ui_Form):
-
def __init__(self):
-
super(MyWindow, self).__init__()
-
self.setupUi(self) # 使用 sjui.Ui_Form 类中的方法初始化 UI
-
self.init_ui()
-
self.sub_window = None # 存储子窗口对象的属性
-
def init_ui(self):
-
self.qsl = QStackedLayout(self.groupBox_2)
-
self.box1 = BOX1()
-
self.box2 = BOX2()
-
self.box3 = BOX3()
-
self.qsl.addWidget(self.box1)
-
self.qsl.addWidget(self.box2)
-
self.qsl.addWidget(self.box3)
-
# 给按钮添加事件(即点击后要调用的函数)
-
self.pushButton.clicked.connect(self.btn_press1_clicked)
-
self.pushButton_2.clicked.connect(self.btn_press2_clicked)
-
self.pushButton_3.clicked.connect(self.btn_press3_clicked)
-
# 设置按钮1的初始样式
-
self.pushButton.setStyleSheet("background-color: #CAE1FF;")
-
def btn_press1_clicked(self):
-
self.qsl.setCurrentIndex(0)
-
self.pushButton.setStyleSheet("background-color: #CAE1FF;")
-
self.pushButton_2.setStyleSheet("") # 恢复按钮2的默认样式
-
self.pushButton_3.setStyleSheet("") # 恢复按钮3的默认样式
-
def btn_press2_clicked(self):
-
self.qsl.setCurrentIndex(1)
-
self.pushButton.setStyleSheet("") # 恢复按钮1的默认样式
-
self.pushButton_2.setStyleSheet("background-color: #CAE1FF;")
-
self.pushButton_3.setStyleSheet("") # 恢复按钮3的默认样式
-
def btn_press3_clicked(self):
-
self.qsl.setCurrentIndex(2)
-
self.pushButton.setStyleSheet("") # 恢复按钮1的默认样式
-
self.pushButton_2.setStyleSheet("") # 恢复按钮2的默认样式
-
self.pushButton_3.setStyleSheet("background-color: #CAE1FF;")
-
def closeEvent(self, event):
-
# 关闭其他窗口的代码
-
for widget in QApplication.topLevelWidgets():
-
if isinstance(widget, QWidget) and widget != self:
-
widget.close()
-
event.accept()
-
if __name__ == '__main__':
-
app = QApplication(sys.argv)
-
w = MyWindow()
-
icon = QIcon('./img/扫描.png')#添加图标
-
w.setWindowIcon(icon)
-
w.show()
-
sys.exit(app.exec_())
我们在box1、box2、box3各把ui里面的代码导入即可,以box1举例
-
from PyQt5.QtWidgets import QWidget
-
from .box1_ui import Ui_Form
-
class BOX1(QWidget,Ui_Form):
-
def __init__(self):
-
super(QWidget, self).__init__()
-
self.setupUi(self) # 使用 sjui.Ui_Form 类中的方法初始化 UI
-
self.init_ui()
4.功能调用介绍
self.init_ui()创建对象时自动调用
init_ui()里面主要为按钮连接的方法
读取文件功能,获取文件路径
-
fileName, fileType = QtWidgets.QFileDialog.getOpenFileName(None, "选取文件", os.getcwd(), "All Files(*.exe);")
-
self.lineEdit.setText(fileName)
创建子进程,这里不创建子进程会把整个程序阻塞
-
# 创建子进程
-
def process_creation(self):
-
self.process = QProcess()
-
self.process.setProcessChannelMode(QProcess.MergedChannels)
-
self.process.readyReadStandardOutput.connect(self.handle_output)
-
self.process.finished.connect(self.handle_finished)
-
self.process.start(self.lineEdit.text())
-
# 命令输出
-
def handle_output(self):
-
try:
-
data = self.process.readAll().data().decode('utf-8').rstrip()
-
# 将命令行输出至文本框
-
self.textEdit.append(data)
-
except:
-
data = self.process.readAll().data().decode('latin-1').rstrip()
-
# 将命令行输出至文本框
-
self.textEdit.append(data)
这里可以对输出的文本进行优化一下颜色,不然是全黑色字体和xray原本输出的颜色不符
通过正则匹配,对不同字段进行不同颜色的输出
-
# 命令输出
-
def handle_output(self):
-
try:
-
data = self.process.readAll().data().decode('utf-8').strip()
-
lines = data.split('\n')
-
for line in lines:
-
if "[INFO]" in line:
-
line = line.replace("[INFO]", f'<span style="color: blue;">[INFO]</span>')
-
elif "[Vuln: dirscan]" in line:
-
line = line.replace("[Vuln: dirscan]", f'<span style="color: red;">[Vuln: dirscan]</span>')
-
elif line.startswith('\t'):
-
line = f'<span style="color: purple;"> {line}</span>'
-
elif re.match(r'^[\u4e00-\u9fff]', line):
-
line = f'<span style="color: red;">{line}</span>'
-
elif "requestSent" in line:
-
line = f'<span style="color: #FFBF00;">{line}</span>'
-
elif "All pending" in line:
-
line = f'<span style="color: #00FF7F;">{line}</span>'
-
self.textEdit.append(line)
-
except:
-
data = self.process.readAll().data().decode('latin-1').strip()
-
lines = data.split('\n')
-
for line in lines:
-
if "[INFO]" in line:
-
line = line.replace("[INFO]", f'<span style="color: blue;">[INFO]</span>')
-
elif "[Vuln: dirscan]" in line:
-
line = line.replace("[Vuln: dirscan]", f'<span style="color: red;">[Vuln: dirscan]</span>')
-
elif line.startswith('\t'):
-
line = f'<span style="color: purple;"> {line}</span>'
-
elif re.match(r'^[\u4e00-\u9fff]', line):
-
line = f'<span style="color: red;">{line}</span>'
-
elif "requestSent" in line:
-
line = f'<span style="color: #FFBF00;">{line}</span>'
-
elif "All pending" in line:
-
line = f'<span style="color: #00FF7F;">{line}</span>'
-
self.textEdit.append(line)
修改配置文件主要在于修改config.yaml文件,这里用import ruamel.yaml这个库。
-
# 确认修改基础配置
-
def basics_save(self):
-
if os.path.exists('file_address.yaml'):
-
with open('config.yaml', 'r', encoding='utf-8') as file:
-
yaml = ruamel.yaml.YAML()
-
config = yaml.load(file)
-
config['parallel'] = int(self.spinBox.text())
-
config['http']['dial_timeout'] = int(self.spinBox_2.text())
-
config['http']['max_redirect'] = int(self.spinBox_3.text())
-
config['http']['max_qps'] = int(self.spinBox_4.text())
-
with open('config.yaml', 'w', encoding='utf-8') as file:
-
yaml.dump(config, file)
-
self.open_tishiwindow(None, None)
-
else:
-
text = '未配置xray文件'
-
img = './img/失败.png'
-
self.open_tishiwindow(text, img)
然后通过子进程去输出命令行,并调用handle_output方法输出到文本
这里通过获取界面的选项来拼接命令行
然后config界面和rad界面同理
-
def initiative_scan(self):
-
if os.path.exists('file_address.yaml'):
-
self.textEdit.clear()
-
self.process_kill()
-
if not hasattr(self, 'is_first_click'):
-
if self.lineEdit_2.text() == "" or self.lineEdit_2.text() == "默认则随机命名":
-
self.dict['name'] = str(int(time.time()))
-
else:
-
self.dict['name'] = self.lineEdit_2.text()
-
# 第一次按下按钮
-
self.is_first_click = True
-
self.process_kill()
-
with open('file_address.yaml', 'r', encoding='utf-8') as file:
-
yaml = ruamel.yaml.YAML()
-
config = yaml.load(file)
-
self.args = config['xray_address']
-
if self.radioButton_5.isChecked():
-
self.args = self.args + ' --log-level debug'
-
if self.radioButton_6.isChecked():
-
self.args = self.args + ' --log-level info'
-
if self.radioButton_7.isChecked():
-
self.args = self.args + ' --log-level warn'
-
if self.radioButton_8.isChecked():
-
self.args = self.args + ' --log-level error'
-
if self.radioButton_9.isChecked():
-
self.args = self.args + ' --log-level fatal'
-
self.args = self.args + ' webscan'
-
if self.radioButton_11.isChecked():
-
self.args = self.args + ' --level medium'
-
if self.radioButton_12.isChecked():
-
self.args = self.args + ' --level high'
-
if self.radioButton_13.isChecked():
-
self.args = self.args + ' --level critical'
-
if self.dict['url']:
-
self.args = self.args + ' --url ' + self.dict['url']
-
if self.dict['request']:
-
self.args = self.args + ' --raw-request ' + self.dict['request']
-
if self.dict['url_list']:
-
self.args = self.args + ' --url-file ' + self.dict['url_list']
-
if self.radioButton.isChecked():
-
self.args = self.args + ' --html-output ' + self.dict['name'] + '.html'
-
self.dict['name_all'] = os.path.dirname(config['xray_address']) + '/' + self.dict['name'] + '.html'
-
if self.radioButton_2.isChecked():
-
self.args = self.args + ' --json-output ' + self.dict['name'] + '.txt'
-
self.dict['name_all'] = os.path.dirname(config['xray_address']) + '/' + self.dict['name'] + '.txt'
-
self.pushButton_3.setText("关闭主动扫描")
-
self.process_creation()
-
self.process.start(self.args)
-
else:
-
# 第二次按下按钮
-
del self.is_first_click # 删除标记
-
self.pushButton_3.setText("开启主动扫描")
-
self.textEdit.clear()
-
self.process_kill()
-
else:
-
text = '未配置xray文件'
-
img = './img/失败.png'
-
self.open_tishiwindow(text, img)
被动代理效果,红色即是扫出的漏洞
打开输出文件
-
# 查看扫描结果
-
def file_look(self):
-
try:
-
if self.dict['name_all']:
-
os.startfile(self.dict['name_all'])
-
else:
-
text = '没有输出文件'
-
img = './img/失败.png'
-
self.open_tishiwindow(text, img)
-
except:
-
text = '没有输出文件'
-
img = './img/失败.png'
-
self.open_tishiwindow(text, img)
加解密界面主要通过self.textEdit.textChanged.connect(self.encrypt_txt)去对输入的文本进行处理
这里可以创建一个线程,去执行加密操作,避免阻塞界面
-
def start_encrypt_txt(self):
-
t = threading.Thread(target=self.encrypt_txt)
-
t.daemon = True
-
t.start()
md5的界面主要通过字典匹配,如果匹配不到,就返回无法解密
-
found_match = False # 标记是否找到匹配的解密结果
-
with open('./dict/top1w-md5.txt', 'r', encoding='utf-8') as fp:
-
for line in fp:
-
if str_txt == line.strip():
-
# 如果找到了匹配的MD5哈希值,我们可以从原始文件中获取相应的单词
-
with open("./dict/top1w.txt", 'r', encoding='utf-8') as wp:
-
for word_line in wp:
-
words = word_line.split()
-
for word in words:
-
if hashlib.md5(word.encode('utf-8')).hexdigest() == str_txt:
-
found_match = True
-
self.textEdit_6.setText(word)
-
break
-
if found_match:
-
break
-
if not found_match:
-
self.textEdit_6.setText('无法解密')
5.把python打包成exe文件
安装pyinstaller
首先我们要先安装Pyinstaller,直接在cmd使用pip命令
-
pip install pyinstaller
2、执行命令,从程序开始的地方打包,生成exe文件
末尾出现Building EXE from EXE-00.toc completed successfully.代表打包成功
打包后在当前路径dist目录下面生成exe文件
-
Pyinstaller -F -w -i 扫描.ico ui.py
一个图形化工具就写完了
免费获取网络安全优质学习资料与干货教程
申明:本账号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法。