Mki's Blog

Python学习(二):沙雕小工具的诞生

沙雕兽,初始化!

首先,无论如何,先让我们的图形界面跑起来再说

import sys
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow

class OnStepCTF(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('CTF沙雕工具')
        self.setGeometry(500,500,500,500)
        self.show()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    tool = OnStepCTF()
    sys.exit(app.exec_())

这段代码是什么意思呢?

首先,既然要做GUI,当然要设置一个窗口的标题啦!

self.setWindowTitle('CTF沙雕工具')  # 设置窗口标题

然后设置窗口合适的大小

self.setGeometry(500,500,500,500) # 设置窗口大小

最后,让它显示在屏幕上

self.show()

至于其他的部分,是使用PyQt5的一些必需操作,本文的主要内容在OnStepCTF这个类里面。

运行,沙雕小工具的形态就有了

1553858111494.png

一键暴打,安排!

哼哼,既然有了外形,那么来充实内在吧。

import sys
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QPushButton, QMessageBox

class OnStepCTF(QMainWindow):
    def __init__(self):
        ···

    def initUI(self):
        # 初始外形
        self.setWindowTitle('CTF沙雕工具')
        self.setGeometry(500,500,500,500)
        # 添加按钮
        Btn_1 = QPushButton('一键暴打出题人', self)
        Btn_1.move(100,100)
        Btn_1.clicked[bool].connect(self.OnstepAction)

        Btn_2 = QPushButton('一键获取flag', self)
        Btn_2.move(100,150)
        Btn_2.clicked[bool].connect(self.OnstepAction)

        Btn_3 = QPushButton('一键打断复读', self)
        Btn_3.move(100,200)
        Btn_3.clicked[bool].connect(self.OnstepAction)

        self.show()

    def OnstepAction(self):
        sender = self.sender()
        action = sender.text()
        if action == '一键暴打出题人':
            reply = '出题人已被击毙'
        elif action == '一键获取flag':
            reply = 'flag is flag{b4608f992f32586e17c334c6633c8925}'
        else:
            reply = '一位群友及时打断了复读'
        message = QMessageBox.question(self,'一个消息',reply)

if __name__ == "__main__":
    ···

首先,添加一个按钮

Btn_1 = QPushButton('一键暴打出题人', self)

QPushButton是PyQt的按钮类,可以定义按钮的value等属性。

移动按钮到合适的位置

Btn_1.move(100,100)

将按钮和事件绑定

Btn_1.clicked[bool].connect(self.OnstepAction)

然后我们来设置具体的事件内容

获取事件的信号源(详细内容参考PyQt5的信号机制,篇幅有限,有机会再来研究)

sender = self.sender()

这里的信号源是一个按钮,顺便获取他的名称

action = sender.text()

设定不同的反馈之后,利用一个messagebox来弹出消息

message = QMessageBox.question(self,'一个消息',reply)

以此类推,可以有很多不同的操作,这里就举几个例子

1553860293205.png

枪毙名单,加急!

到此我们还不能满足,因为万恶的出题人是无穷无尽的,我们有时需要手动输入名单,一个一个枪毙。

import sys
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QPushButton, QMessageBox, QLabel, QInputDialog

class OnStepCTF(QMainWindow):
    def __init__(self):
        ···

    def initUI(self):
        # 初始外形
            ···
        #  一键按钮
            ···

        # 加急名单
        self.Label_1 = QLabel(self)
        text = "出题人A\n出题人B\n出题人C\n出题人D\n"
        self.Label_1.setText(text)
        self.Label_1.setGeometry(300,90,100,200)
        self.Btn_4 = QPushButton('枪毙名单', self)
        self.Btn_4.move(300,50)
        self.Btn_4.clicked[bool].connect(self.ShowPerson)

        self.show()

    def ShowPerson(self):
        text, ok = QInputDialog.getText(self,"本月加急","name: ")
        if ok:
            newtext = self.Label_1.text() + str(text) + "\n"
            self.Label_1.setText(newtext)


    def OnstepAction(self):
        ···

if __name__ == "__main__":
    ···

首先整一个label来显示名单,然后设置label的内容

self.Label_1 = QLabel(self)
text = "出题人A\n出题人B\n出题人C\n出题人D\n"
self.Label_1.setText(text)

利用一个按钮去产生一个消息框,获取新名字,最后更改label的内容

text, ok = QInputDialog.getText(self,"本月加急","name: ")

1553864827894.png

来点乐子吧!

当师傅们做不出题目的时候,他们会做什么呢?没错,他们会打开bilibili,打开fgo,甚至某我现在还叫不出名字的狙击游戏。。。

反正就是很快乐。

# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QPushButton, QMessageBox, QLabel, QInputDialog
from PyQt5.QtGui import QPalette, QPixmap

class OnStepCTF(QMainWindow):
    def __init__(self):
        ···

    def initUI(self):
        # 初始外形
        self.setWindowTitle('CTF沙雕工具 v1.0 by Mki603')
        self.setGeometry(100,100,500,800)

        #  一键按钮
        ···

        # 加急名单
        ···

        # 找点乐子
        Bilibili = QLabel(self)
        Bilibili.setText('<a href="www.bilibili.com">bilibili ( ゜ -゜)つロ 乾杯~</a>')
        Bilibili.setOpenExternalLinks(True)
        Bilibili.setGeometry(100,200,300,500)

        BilibiliTV = QLabel(self)
        BilibiliTV.setPixmap(QPixmap('C:\\xxx\\bilibili.png'))
        BilibiliTV.setScaledContents(True)
        BilibiliTV.setGeometry(100,240,200,200)

        Game = QLabel(self)
        Game.setText('<a href="#">内网五子棋!启动!</a>')
        Game.setGeometry(600,200,300,500)

        GameBox = QLabel(self)
        GameBox.setPixmap(QPixmap('C:\\xxx\\fivechess.png'))        
        GameBox.setScaledContents(True)
        GameBox.setGeometry(600,240,200,200)

        self.show()

    def ShowPerson(self):
        ···

    def OnstepAction(self):
        ···

if __name__ == "__main__":
    ···

QLabel有很多用处

比如整一个超链接,并把它设置成可以点击

Bilibili = QLabel(self)
Bilibili.setText('<a href="www.bilibili.com">bilibili ( ゜ -゜)つロ 乾杯~</a>')
Bilibili.setOpenExternalLinks(True)   # 允许跳转

或者可以是放置图片

BilibiliTV = QLabel(self)
BilibiliTV.setPixmap(QPixmap('C:\\xxx\\bilibili.png'))
BilibiliTV.setScaledContents(True)  # 设定自适应label大小

其他同理,我这里调整了一下窗口大小。

1553868159242.png

额…感觉有点丑?

不是,能用就行了哇,怎么要求这么高啊(逃

算了,还是美化一下吧。

# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QPushButton, QMessageBox, QLabel, QInputDialog
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QGridLayout, QGroupBox
from PyQt5.QtGui import QPalette, QPixmap

class OnStepCTF(QWidget):   # 父类改成了QWidget
    def __init__(self):
        ···

    def initUI(self):
        #初始外形
        self.setWindowTitle('CTF沙雕工具 v1.0 by Mki603')

        #  一键按钮
        self.Btn_1 = QPushButton('一键暴打出题人', self)
        self.Btn_1.clicked[bool].connect(self.OnstepAction)

        self.Btn_2 = QPushButton('一键获取flag', self)
        self.Btn_2.clicked[bool].connect(self.OnstepAction)

        self.Btn_3 = QPushButton('一键打断复读', self)
        self.Btn_3.clicked[bool].connect(self.OnstepAction)

        self.Btn_4 = QPushButton('一键开启复读', self)
        self.Btn_4.clicked[bool].connect(self.OnstepAction)

        self.Btn_5 = QPushButton('一键膜dalao', self)
        self.Btn_5.clicked[bool].connect(self.OnstepAction)

        # 加急名单
        self.Label_1 = QLabel(self)
        text = "出题人A\n出题人B\n出题人C\n出题人D\n"
        self.Label_1.setText(text)
        self.Btn_Add = QPushButton('添加名单', self)
        self.Btn_Add.clicked[bool].connect(self.ShowPerson)

        # 找点乐子
        self.Bilibili = QLabel(self)
        self.Bilibili.setText('<a href="www.bilibili.com">bilibili ( ゜ -゜)つロ 乾杯~</a>')
        self.Bilibili.setOpenExternalLinks(True)

        self.BilibiliTV = QLabel(self)
        self.BilibiliTV.setPixmap(QPixmap('C:\\Users\\Mki_6\\Desktop\\Code\\Pic\\bilibili.png'))
        self.BilibiliTV.setScaledContents(True)

        self.Chess = QLabel(self)
        self.Chess.setText('<a href="#">内网五子棋!启动!</a>')

        self.ChessPic = QLabel(self)
        self.ChessPic.setPixmap(QPixmap('C:\\Users\\Mki_6\\Desktop\\Code\\Pic\\five.png'))
        self.ChessPic.setScaledContents(True)

        # 弹性布局
        self.setGeometry(100,100,900,400)
        self.CreateGridLayout()
        self.setLayout(self.windowLayout)
        self.show()

    def CreateGridLayout(self):
        self.BtnBox = QGroupBox("快捷按钮")
        layout = QGridLayout()
        layout.addWidget(self.Btn_1,1,1)
        layout.addWidget(self.Btn_2,2,1)
        layout.addWidget(self.Btn_3,3,1)
        layout.addWidget(self.Btn_4,4,1)
        layout.addWidget(self.Btn_5,5,1)
        self.BtnBox.setLayout(layout)

        self.ListBox = QGroupBox("枪毙名单")
        layout = QGridLayout()
        layout.addWidget(self.Label_1,1,2)
        layout.addWidget(self.Btn_Add,2,2)
        self.ListBox.setLayout(layout)

        self.GameBox = QGroupBox("摸鱼入口")
        layout = QGridLayout()
        layout.addWidget(self.Bilibili,1,1)
        layout.addWidget(self.BilibiliTV,1,2)
        layout.addWidget(self.Chess,2,2)
        layout.addWidget(self.ChessPic,2,1)
        self.GameBox.setLayout(layout)

        self.windowLayout = QHBoxLayout()
        self.windowLayout.addWidget(self.BtnBox)
        self.windowLayout.addWidget(self.ListBox)
        self.windowLayout.addWidget(self.GameBox)

    def ShowPerson(self):
        ···

    def OnstepAction(self):
        ···

if __name__ == "__main__":
    ···

关键在于设置弹性布局

首先新建一个函数来安排布局

def CreateGridLayout(self):

然后把元素放到一个QGridLayout()类里面去,再把一个QGroupBox()的布局设置为当前layout

self.BtnBox = QGroupBox("快捷按钮")
layout = QGridLayout()
layout.addWidget(self.Btn_1,1,1)
layout.addWidget(self.Btn_2,2,1)
layout.addWidget(self.Btn_3,3,1)
layout.addWidget(self.Btn_4,4,1)
layout.addWidget(self.Btn_5,5,1)
self.BtnBox.setLayout(layout)

集合多个box,把这样的布局命名为windowLayout

self.windowLayout = QHBoxLayout()
self.windowLayout.addWidget(self.BtnBox)
self.windowLayout.addWidget(self.ListBox)
self.windowLayout.addWidget(self.GameBox)

最终在主函数里把windowLayout设置成当前窗口的布局

self.CreateGridLayout()
self.setLayout(self.windowLayout)

这里有一个问题要注意,是关于class OnStepCTF()的父类。在使用弹性布局之前,无论是继承QMainWindow还是QWeight,GUI都是正常显示的,但是使用了弹性布局之后,使用前者则不会显示任何元素,必须把父类改成Qweights或者QDialog(查了下发现这甚至对信号和槽也有影响。

效果如下。

1553927898172.png

还不满足的话。。。出门左转QtDesigner或其他快乐的方式。。。。。

结尾吐糟环节

都是假的,其实是因为密码学实验被强制要求使用GUI,趁此机会发扬沙雕精神,复现表情包。

希望之后能不断地添加功能吧。

以上。