520表白小程式設計Python程式碼詳解(PyQt5介面,B站動漫風)

思緒無限發表於2022-05-23
功能演示動圖

摘要:介紹一個動漫風的表白小程式,介面使用Python以及PyQt實現,介面樣式經過多次美化調整,使得整體清新美觀。本文詳細介紹程式碼設計和實現過程,不僅是居家表白必備,而且適合新入門的朋友學習介面設計,完整程式碼資原始檔請轉至文末的下載連結。本博文目錄如下:


➷點選跳轉至文末所有涉及的完整程式碼檔案下載頁☇


程式碼介紹及演示視訊連結:https://www.bilibili.com/video/BV1q3411G7vB/(正在更新中,歡迎關注博主B站視訊)


前言

        今年的520準備寫一個表白程式,畢竟程式設計師一般都喜歡在這一天來點自己的特色,不過大多都是喜聞樂見的那一套程式碼。雖然表白是用不上的,但是還是可以學習一番的,既然如此,不如來點賞心悅目的,那就有了這篇博文的內容。想法是看到網上有個類似的移動按鈕的表白小程式,點子是好的,不過介面有些簡陋,我就自己重新設計了一個。從背景圖到按鈕製作都是用Photoshop和PPT設計的,三個介面的截圖如下:

功能演示動圖

        風格上是借鑑了B站的風格和配色,包括背景的粉紅色、動漫人像等,當然圖片主要來自網上,我利用Photoshop進行了摳圖和美化處理。整體上依然保持博主一貫的清新簡約風,不過既然是表白程式,主題當然要粉紅甜蜜,至於細節之處大家也可以有自己的見解。

         PS:表白有風險,編碼需謹慎!表白程式只是趣味學習的程式碼,真正表白還是應該當面表達心聲。儘管520已過,但是我還是補上這個程式碼吧,希望大家都能找到心中所愛,勇往直前!(別問我520怎麼不發,520誰敲程式碼啊)


1. 準備工作

(一)設計思路

        程式的設計思路是一個粉紅的表白介面,別人在開啟程式後,可以點選按鈕回應:“我愛你”、“不愛你”,點同意彈出2號介面,即表白成功;若不同意,滑鼠滑動到“不愛你”按鈕,該按鈕自動跑開,別人是點選不到的;如果不想選,點選關閉視窗,則彈出3號視窗,請求再給一次機會。(其實有點無賴哈)

功能演示動圖

        介面設計上,主介面用QtDesigner拖出背景label和兩個按鈕,另外兩個介面同樣的搭配Label和按鈕。在邏輯上,點選“我愛你”按鈕彈出2號視窗;監聽滑鼠位置,出現在“不愛你”附近則移動該按鈕的位置;修改關閉事件的槽函式,使其開啟3號介面,忽視程式關閉。

(二)圖片準備

        準備幾張處理過的動漫圖片,需要用到具有透明背景的優美圖片。一般難找到透明背景的,所以使用Photoshop將動漫人物主體摳出,摳出的人物圖片如下圖所示:

功能演示動圖

        摳出人物主體的圖片可以任意疊加在介面中,更能體現立體感,至於摳圖軟體大家隨意,沒必要因為Photoshop就勸退。同樣的,我們以此摳出以下的幾張圖片,都是儲存為透明背景的圖片。

功能演示動圖

        我們還需要準備一些文字和按鈕,以下文字按鈕通過PPT製作,當然Photoshop也是可以的,用什麼軟體你開心就好,設計美觀就夠了。

功能演示動圖
功能演示動圖

2. 介面設計

        介面採用QtDesigner設計,新建love520.ui檔案,向介面中拖入4個Label控制元件,並擺置成如下圖所示的效果。並設定介面的qss樣式,包括按鈕的圖示、label的背景圖等等。

功能演示動圖

        設定MainWindow的樣式表如下,滑鼠劃過或選中時label和按鈕有抖動的效果:

QLabel{border:10px;}
QLabel::hover {
border:0px;}

QToolButton {
	background-color: transparent;
	borde: 20px;	
}
QToolButton:hover {
	border: 0px solid rgb(255, 255, 255);
}
QToolButton:pressed {	
	background-color: transparent;
	border: 5px;	
}

        對每個label設定各自的背景圖片,如label_2的背景樣式為下圖。依次開啟label的styleSheet屬性,設定前面準備的背景圖片。經過樣式和背景圖片設定後,當滑鼠滑動時圖片或按鈕有放大的效果,如下圖所示:

border-image: url(:/back/girl-2.png);
功能演示動圖

        對於按鈕可以設定其icon屬性為前期設計的按鈕圖示,調整尺寸並設定樣式如下,主要是滑鼠懸停和點選後的樣式情況,調整邊界值使其具有縮放效果。

QToolButton {
	background-color: transparent;
	borde: 20px;	
}
QToolButton:hover {
	border: 0px solid rgb(255, 255, 255);
}
QToolButton:pressed {	
	background-color: transparent;
	border: 5px;	
}

        對於點選主介面關閉按鈕後需要彈出的子視窗,設計如下,包括動漫人物圖片、文字、按鈕的擺放如下,其qss樣式與主介面的類似,這裡不再贅述。

功能演示動圖

        同樣的點選“我愛你”按鈕後,需要彈出的子視窗設計如下,這時就只有label了,畢竟已經得手了,可以給一個慶祝送花的介面。

功能演示動圖

3. 程式碼編寫

    介面準備完畢,我們可以用PyUIC工具將上一節設計完成的ui檔案轉換為py檔案,這樣可以直接呼叫該視窗的類和方法。另外,使用PyRcc工具可以將ui檔案中涉及的qrc資原始檔轉換為py檔案,方便對圖片背景等檔案進行讀取。這裡是一種邏輯介面分離的方式,程式碼邏輯和介面是分開的,這樣對介面進行調整時,功能的邏輯程式碼就無需多做調整。

(一)主視窗介面

    主視窗匯出的視窗介面程式碼如下:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'love520.ui'
# author: sixuwuxian
# Created by: PyQt5 UI code generator 5.15.4
# website: wuxian.blog.csdn.net
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1308, 789)
        MainWindow.setMinimumSize(QtCore.QSize(1308, 789))
        MainWindow.setMaximumSize(QtCore.QSize(1308, 789))
        MainWindow.setStyleSheet("QLabel{border:10px;}\n"
"QLabel::hover {\n"
"border:0px;}\n"
"\n"
"QPushButton {\n"
"    border:5px;}\n"
"\n"
"QPushButton:pressed {    \n"
"    background-color: transparent;\n"
"    border-radius: 0px;    \n"
"}\n"
"")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(42, 174, 1235, 575))
        self.label.setStyleSheet("QLabel {\n"
"    border:10px;\n"
"    border-image: url(:/back/back-image.png);\n"
"}")
        self.label.setText("")
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(42, -2, 547, 751))
        self.label_2.setStyleSheet("border-image: url(:/back/girl-2.png);\n"
"")
        self.label_2.setText("")
        self.label_2.setObjectName("label_2")
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(420, -2, 847, 211))
        self.label_3.setStyleSheet("border-image: url(:/back/love-text.png);")
        self.label_3.setText("")
        self.label_3.setObjectName("label_3")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(1126, 542, 191, 213))
        self.label_4.setStyleSheet("border-image: url(:/back/baby-1.png);")
        self.label_4.setText("")
        self.label_4.setObjectName("label_4")
        self.toolButton = QtWidgets.QToolButton(self.centralwidget)
        self.toolButton.setGeometry(QtCore.QRect(564, 428, 225, 117))
        self.toolButton.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.toolButton.setStyleSheet("QToolButton {\n"
"    background-color: transparent;\n"
"    borde: 20px;    \n"
"}\n"
"QToolButton:hover {\n"
"    border: 0px solid rgb(255, 255, 255);\n"
"}\n"
"QToolButton:pressed {    \n"
"    background-color: transparent;\n"
"    border: 5px;    \n"
"}")
        self.toolButton.setText("")
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(":/back/yes.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.toolButton.setIcon(icon)
        self.toolButton.setIconSize(QtCore.QSize(400, 400))
        self.toolButton.setObjectName("toolButton")
        self.toolButton_2 = QtWidgets.QToolButton(self.centralwidget)
        self.toolButton_2.setGeometry(QtCore.QRect(872, 426, 225, 115))
        self.toolButton_2.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.toolButton_2.setStyleSheet("QToolButton {\n"
"    background-color: transparent;\n"
"    borde: 20px;    \n"
"}\n"
"QToolButton:hover {\n"
"    border: 0px solid rgb(255, 255, 255);\n"
"}\n"
"QToolButton:pressed {    \n"
"    background-color: transparent;\n"
"    border: 5px;    \n"
"}")
        self.toolButton_2.setText("")
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap(":/back/no.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.toolButton_2.setIcon(icon1)
        self.toolButton_2.setIconSize(QtCore.QSize(400, 400))
        self.toolButton_2.setObjectName("toolButton_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1308, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Bilibili: https://space.bilibili.com/456667721"))
import image_back_rc

(二)關閉視窗彈窗

    當點選主視窗關閉按鈕後,彈出的視窗介面程式碼如下:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'love520.ui'
# author: sixuwuxian
# Created by: PyQt5 UI code generator 5.15.4
# website: wuxian.blog.csdn.net
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.setWindowModality(QtCore.Qt.ApplicationModal)
        Dialog.resize(743, 415)
        Dialog.setMinimumSize(QtCore.QSize(743, 415))
        Dialog.setMaximumSize(QtCore.QSize(743, 415))
        Dialog.setStyleSheet("")
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(2, 0, 335, 415))
        self.label.setStyleSheet("border-image: url(:/back/close-1.jpeg);")
        self.label.setText("")
        self.label.setObjectName("label")
        self.toolButton = QtWidgets.QToolButton(Dialog)
        self.toolButton.setGeometry(QtCore.QRect(436, 276, 205, 77))
        self.toolButton.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
        self.toolButton.setStyleSheet("QToolButton {\n"
"    background-color: transparent;\n"
"    borde: 10px;    \n"
"}\n"
"QToolButton:hover {\n"
"    border: 0px solid rgb(255, 255, 255);\n"
"}\n"
"QToolButton:pressed {    \n"
"    background-color: transparent;\n"
"    border: 5px;    \n"
"}")
        self.toolButton.setText("")
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(":/back/choose.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.toolButton.setIcon(icon)
        self.toolButton.setIconSize(QtCore.QSize(400, 400))
        self.toolButton.setObjectName("toolButton")
        self.label_2 = QtWidgets.QLabel(Dialog)
        self.label_2.setGeometry(QtCore.QRect(352, 78, 377, 145))
        self.label_2.setStyleSheet("border-image: url(:/back/choose-text.png);")
        self.label_2.setText("")
        self.label_2.setObjectName("label_2")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "考慮一下吧"))
import image_back_rc

(三)按鈕點選彈窗

    點選“我愛你”按鈕,彈出的子視窗介面程式碼如下:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'love520.ui'
# author: sixuwuxian
# Created by: PyQt5 UI code generator 5.15.4
# website: wuxian.blog.csdn.net
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(680, 401)
        Dialog.setMinimumSize(QtCore.QSize(680, 401))
        Dialog.setMaximumSize(QtCore.QSize(680, 401))
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(2, 12, 323, 389))
        self.label.setStyleSheet("border-image: url(:/back/loved-2.png);")
        self.label.setText("")
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(Dialog)
        self.label_2.setGeometry(QtCore.QRect(360, 144, 271, 269))
        self.label_2.setStyleSheet("border-image: url(:/back/flower.png);")
        self.label_2.setText("")
        self.label_2.setObjectName("label_2")
        self.label_3 = QtWidgets.QLabel(Dialog)
        self.label_3.setGeometry(QtCore.QRect(302, 20, 343, 145))
        self.label_3.setStyleSheet("border-image: url(:/back/loveyou.png);")
        self.label_3.setText("")
        self.label_3.setObjectName("label_3")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "我也喜歡你"))
import image_back_rc

(四)介面視窗邏輯

    要想實現前面演示的功能,需要呼叫以上三個視窗的類和方法,並新增一些額外的功能。由於邏輯介面分離,我們不會直接修改以上匯出的程式碼,這就要用到類繼承的方式。首先我們匯入需要用到的依賴:

import random
from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog
from PyQt5.QtWidgets import QDesktopWidget

from ps.closeUI import Ui_Dialog
from ps.love520 import Ui_MainWindow
from ps.loveyou import Ui_Dialog as Ui_YesDialog

    以上程式碼最後三行分別表示匯入關閉主介面的彈窗、主介面視窗、點選按鈕彈窗的類。我們新建兩個類分別命名為CloseWindow、YesWindow,它們均繼承QDialog類,以及Ui_Dialog和Ui_YesDialog這兩個子視窗,初始化時繼承原有構造方法,增加setupUi以及retranslateUi方法呼叫,以建立子視窗控制元件。然後重寫Ui_YesDialog中的close方法,使其關閉時關閉所有事件。

class CloseWindow(QDialog, Ui_Dialog):
    def __init__(self):
        super(CloseWindow, self).__init__()
        self.setupUi(self)
        self.retranslateUi(self)
        self.toolButton.clicked.connect(self.close)


class YesWindow(QDialog, Ui_YesDialog):
    def __init__(self):
        super(YesWindow, self).__init__()
        self.setupUi(self)
        self.retranslateUi(self)

    def close(self):
        sender = self.sender()
        event = QApplication.instance()
        event.quit()

    主視窗繼承QMainWindow和前面設計的Ui_MainWindow類,初始化方法中除了setupUi和retranslateUi方法,還例項化CloseWindow、YesWindow的物件,以便後面呼叫。為兩個按鈕繫結了事件的槽函式,分別是“我愛你”按鈕(toolButton)的點選事件槽函式、“不愛你”按鈕(toolButton_2)的滑鼠事件監聽。

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, *args, obj=None, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setupUi(self)
        self.retranslateUi(self)  # 介面控制元件
        # self.center()

        # self.setWindowFlags(QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
        # self.setWindowTitle("我喜歡你")

        self.toolButton.clicked.connect(self.trueEvent)
        self.toolButton_2.installEventFilter(self)
        # self.toolButton_2.move(300, 400)
        # self.toolButton_2.move(300, 400)
        self.myDialog = CloseWindow()
        self.yesDialog = YesWindow()

    def trueEvent(self):
        self.yesDialog.show()
        self.hide()
        # sender = self.sender()
        # event = QApplication.instance()
        # event.quit()

    def eventFilter(self, object, event):
        if event.type() == QtCore.QEvent.HoverMove:
            self.toolButton_2.move(random.randint(472, 918),
                                   random.randint(174, 624))
            return True
        return False

    def closeEvent(self, event):
        # reply = QMessageBox.question(self.centralwidget, "小姐姐", "不能逃避的哦", QMessageBox.Yes, QMessageBox.Yes)
        # myDialog = ChildWindow()
        self.myDialog.show()
        event.ignore()
        # if reply == QMessageBox.Yes:
        #     event.ignore()

    def center(self):
        screen = QDesktopWidget().screenGeometry()  # 計算顯示螢幕的大小
        size = self.geometry()  # 用來獲取視窗的大小
        self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2)  # 將視窗移動到螢幕中央

    以上程式碼中設計的幾個方法,其功能如下:

  1. trueEvent方法:toolButton按鈕被點選時執行該函式,顯示yesDialog(表白成功子視窗)介面,同時隱藏主介面;
  2. eventFilter方法:首先判斷事件型別,當滑鼠懸停移動時,將toolButton按鈕的位置移動到一定範圍的隨機座標,實現“不愛你”按鈕的跑路功能;
  3. closeEvent方法:主介面的關閉按鈕被點選時呼叫該方法,方法中顯示myDialog視窗,同時將關閉視窗事件忽略,即阻止視窗關閉;
  4. center方法:此方法呼叫時可以將主介面移動到螢幕的中心位置。

(五)主程式的呼叫

    以上類方法實現後,則可以呼叫MainWindow類,將主介面顯示在系統介面中。如下程式碼實現程式碼呼叫:

# -*- coding: utf-8 -*-
# @Time    : 2022/05/20 10:06
# @Author  : sixuwuxian
# @Email   : sixuwuxian@aliyun.com
# @blog    : wuxian.blog.csdn.net
# @Software: PyCharm

from sys import argv, exit
from PyQt5.QtWidgets import QApplication
from loveUI import MainWindow

if __name__ == '__main__':

    app = QApplication(argv)

    win = MainWindow()
    win.show()
    exit(app.exec_())

4. 下載連結

    若您想獲得博文中涉及的實現完整全部程式檔案(包括測試圖片、視訊,py, UI檔案等,如下圖),這裡已打包上傳至博主的百度雲盤,有需要的朋友可關注本人B站(思緒亦無限)公眾號(AI技術研究與分享)後,回覆:love520 獲取。

功能演示動圖

Python版本:3.8,請勿使用其他版本,需要安裝的依賴:PyQt5 == 5.15.5

PS:可獨立執行的exe檔案同樣包含在分享的完整程式碼包中。


結束語

        由於博主能力有限,博文中提及的方法即使經過試驗,也難免會有疏漏之處。希望您能熱心指出其中的錯誤,以便下次修改時能以一個更完美更嚴謹的樣子,呈現在大家面前。同時如果有更好的實現方法也請您不吝賜教。

相關文章