目錄
前言
配置環境
正式開發
開發前準備
界面制作
踩坑日記一
邏輯與界面分離
代碼撰寫
踩坑日記二
踩坑日記三
踩坑日記四
程式封裝
最終效果
后記
前言
本人最近接到一個小任務,用python完成一個垃圾分類小程式,功能要求如下:用戶可以通過點擊不同的垃圾桶判斷螢屏上出現的垃圾種類,判斷對錯,普及垃圾分類知識,
說來有趣,本人也是剛剛轉行到程式員行列,各種知識學習都還不夠,只能一邊做一邊學,網上找各種教程學習,也是踩了不少坑,不過好在最后花了一點時間把任務完成了,作為一名初學者,覺得還是有必要記錄下辛酸歷程,為后面的學習之路做個鋪墊,話不多說,開整!
首先介紹一下作業環境:
- Windows 10
- Python 3.9
- pycharm2020.1
- PyQt5
- QTdesigner
配置環境
首先就是安裝各種環境,這里不多贅述,其他博主已經介紹的很詳細了(但是本人并沒有在pycharm中安裝QTdesigner和ui轉py的外部工具,這個后面會講到)要注意的是python版本的問題在安裝PyQT5的時候命令不一樣
pip install PyQt5
pip3 install PyQt5
正式開發
開發前準備
第一步是理清思路(這點很重要,敲黑板劃重點):
這里的思路包括整個專案的進行流程,界面之間的跳轉邏輯,功能實作中的函式邏輯,后面都會詳細講到,這里就不講那么多,但是再次強調這一步真的很重要,且對于邏輯能力不強的同學來說,好記性不如爛筆頭,QAQ
界面制作
鑒于之前接觸過C++的QT可視化界面開發,所以本人第二步選擇制作出所有的界面,鑒于QT相對來說比漸簡單,拖拖拽拽就可以形成還不錯的界面,這里就簡單介紹一下部分控制元件的屬性設定,
先來看看本人最后完成的界面(由于本次專案要求不高,所以界面并沒有進行太多的美化,各位大佬可以利用QSS等對界面進行美化)

這里就不講具體制作,只選擇講幾個我覺得比較重要的屬性,
1、最小最大尺寸

很好理解的屬性,這里筆者建議想要獲得更好的視覺效果最好是將界面的最最小最大尺寸都進行設定,這樣將會避免因為拖動造成的不必要的界面美觀問題,
2、label的部分屬性

筆者界面的背景選擇了label來進行設定,在label中顯示圖片的方法就是在pixmap屬性中選擇對應的圖片,這里筆者遇到了第一個坑,在QT中是需要加入資源檔案才能呼叫圖片的,但是在后續的應用程式打包程序中,筆者發現qrc資源檔案在打包時有更多繁瑣的步驟,所以在回傳來重新檢查的時候筆者發現,只要在py代碼中將圖片使用絕對地址呼叫就不存在這個問題,簡答好用,所以在這里筆者就不介紹資源檔案的使用了,網上有很多教程,但是我個人覺得新手還是不要使用了,土方法:使用絕對路徑有時候已經夠用了,scaledContents屬性是讓內容自適應label標簽的大小,建議大家勾選,alignment屬性是決定內容的對齊方式,分別由水平和垂直方向,簡單易懂,
3、控制元件的名字
建議大家在QT設計師里面制作頁面時候就將控制元件的名稱制定好,不要使用它默認的名字,例如label1,label2,label3,pushbutton1等等,最好是能統一命名并且記錄下來,不然在后面的代碼部分將會十分痛苦(包括各個檔案的命名,養成好習慣,英語不好就用自己能懂的方式),一般可以直接在物件查看器中進行修改,

其實視窗的名字也是可以直接進行設定的,就是這個屬性,如果你也和筆者一樣是個強迫癥,建議你修改一下hhhhhhhh
![]()
4、布局方式
布局方式的選擇主要看各位的習慣,QT中有四種布局方式可以選擇,網上教程也很多,這里不贅述
5、其他屬性
至于其他屬性,筆者不多贅述,想了解的自行百度,本次沒有用到,(筆者好像什么也沒講,hhhhh,重點不在這)
6、信號與槽
信號(Signal)和槽(Slot)是Qt中的核心機制,也是在PyQt編程中物件之間進行通信的機制,在QT中,每個繼承自QObject的物件都可以使用信號和槽機制來進行通信, 信號和槽函式通過object.signal.connect()方法來連接,當一個QObject物件發射信號,與之相連接的槽函式將會自動執行,
信號和槽是可以在QTdesigner中設定的,缺點是自帶的槽函式并不多無法實作想要的功能,所以筆者沒有選擇這么做,原因是方便后面的代碼邏輯操作,
到此,界面的設計就基本上結束了,這也是本次專案中最為簡單的一部分,下面進入踩坑高發區,
踩坑日記一:
在將界面匯出為ui檔案后,面臨著第一大問題,怎么將ui檔案轉化問可編輯的py檔案,網上介紹了很多方法,例如pycharm中匯入外部工具,vscode插件以及命令列操作,這些方式筆者都嘗試過,其實并沒有誰好誰不好,結果都是一樣的,但是有這么幾個問題,第一,pycharm利用外部工具,配置方法網上有,但是筆者一直沒有成功,也找不出來是什么原因,遂放棄,第二,vscode插件,這個是比較簡單方式,但是缺點是要切換到另一個軟體操作,有時候并不是很方便,遂放棄,最后筆者采用了命令列的操作方,詳細介紹一下:其實作業系統上正確配置python環境之后,會自帶有轉化命令——pyuic5,下面介紹一下怎么轉化:
pyuic5 -o main.py main.ui
-
-o是操作引數,表示要生成一個檔案
-
main.py是要生成的.py檔案
-
main.ui是在此之前用Qt生成的包含UI設計的.ui檔案
-
一定要在ui檔案的目錄下運行(可以在cmd中利用cd命令轉到檔案夾,也可以直接在打開的檔案夾地址行輸入cmd直接打開當前目錄下的cmd命令)
直接運行此行就可以在目錄中生成對應的py檔案(其實也可以在后面在加上-x,表示直接生成可執行檔案,會幫你將ui檔案直接轉化為可以運行的代碼,但是筆者并沒有這么選擇,這里就涉及到邏輯與界面分離的思想,后面會講到)
邏輯與界面分離
這是一種思想,其實將邏輯代碼直接寫到ui生成的py檔案中也不是不行,但是我們要注意到,如果我們需要更改ui設計,就需要重新將ui檔案轉化為py檔案,這時候之前寫的邏輯代碼就會被覆寫掉,得不償失,采用復制粘貼也不是不行,但是筆者建議,采用邏輯與代碼分離的思想來做,將所有的界面檔案都不做更改,另外新建一個py檔案來呼叫之前ui界面檔案,將功能寫到這個檔案中,需要時呼叫其他的界面檔案即可,
代碼撰寫
到此,我們已經做完了所有準備作業,生成了5個界面ui檔案并且轉化為py檔案,新建了一個main.py來進行邏輯功能的實作,呼叫其他的界面檔案實作界面,
話不多說直接上代碼:
import sys,os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
#匯入UI界面
from welcome import Ui_welcome
from main import Ui_Form
from knowledge import Ui_knowledge
from right import Ui_right
from mistake import Ui_mistake
#標識說明:0=可回收垃圾、1=廚余垃圾、2=有害垃圾、3=其他垃圾#
#比對庫
data = {"碎玻璃酒瓶":0,"蘋果核":1,"易拉罐":0,"塑料瓶":0,"香蕉皮":1,"雞蛋殼":1,"污損垃圾袋":2,
"廢紙":0,"一次性塑料杯":2,"螺絲刀":0,"舊衣服":3,"紙尿褲":3,"煙頭":3,"大骨棒":3,
"電池":2,"過期藥品":2,"節能燈":2,"水銀溫度計":2,"殺蟲劑":2,"灰土":3}
datas = ["水銀溫度計","碎玻璃酒瓶","紙尿褲", "蘋果核", "易拉罐", "塑料瓶", "污損垃圾袋",
"殺蟲劑", "一次性塑料杯", "螺絲刀", "舊衣服","廢紙", "煙頭", "大骨棒",
"電池","雞蛋殼", "過期藥品","香蕉皮" "節能燈", "灰土"]
#開始頁
class MainWindow(QDialog,Ui_welcome):
def __init__(self):
super(MainWindow,self).__init__()
self.setupUi(self)
self.join.clicked.connect(self.join_main)#將信號連接到自定義槽函式join_main
self.exit3.clicked.connect(lambda :self.close())#退出按鈕連接到close函式
def join_main(self):#定義槽函式
self.main = MainWindow1()
self.main.show()#打開主界面
self.close()#關倍訓迎頁
#主界面
class MainWindow1(QMainWindow,Ui_Form):
def __init__(self):
super(MainWindow1,self).__init__()
self.setupUi(self)
self.index = 0
self.knowledge.clicked.connect(self.join_knowledge)#將信號連接到自定義槽函式join_knowledge
self.start.clicked.connect(self.start_game)#將信號連接到自定義槽函式start_game
#將各個按鈕連接到函式game
# 廚余 1
self.PB1.clicked.connect(lambda :self.game(1))
# 可回收 0
self.PB2.clicked.connect(lambda :self.game(0))
# 有害 2
self.PB3.clicked.connect(lambda :self.game(2))
# 其他 3
self.PB4.clicked.connect(lambda :self.game(3))
def game(self,index):
#宣告資料庫,以便于在函式內使用
global data,datas
ans = self.lineEdit.text() #將文本框里面的內容賦值給ans 方便下面判斷對錯
#正確 彈出正確視窗
if index == data[ans]:#與資料庫中對應鍵值對進行比對(自定義字典)
self.main1 = MainWindow3()
self.main1.a.connect(self.next)#將信號源連接到下一題 next
self.main1.show()
else:
self.main1 = MainWindow4()#回答錯誤將不會切換下一題 僅僅彈出答錯界面
self.main1.show()
#下一題
def next(self,index):
global datas,data
self.index +=1 #自增
self.lineEdit.setText(datas[self.index]) #顯示下一題
def start_game(self):
global data,datas
self.lineEdit.setText(datas[0])#顯示題目
def join_knowledge(self):
self.main= MainWindow2()
self.main.show()
#知識界面
class MainWindow2(QDialog,Ui_knowledge):
def __init__(self):
super(MainWindow2,self).__init__()
self.setupUi(self)
#正確界面
class MainWindow3(QDialog,Ui_right):
a = pyqtSignal(str)
def __init__(self):
super(MainWindow3,self).__init__()
self.setupUi(self)
self.exit2.clicked.connect(self.chuan)
def chuan(self):
self.a.emit("正確")
self.close()
#錯誤界面
class MainWindow4(QDialog,Ui_mistake):
def __init__(self):
super(MainWindow4,self).__init__()
self.setupUi(self)
self.exit1.clicked.connect(self.chuan)
def chuan(self):
self.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
以上就是我的main.py 檔案,代碼都進行了注釋,應該還是比較好懂的,大致介紹一下:
1、匯入庫函式和其他界面檔案
#匯入庫函式
import sys,os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
#匯入UI界面
from welcome import Ui_welcome
from main import Ui_Form
from knowledge import Ui_knowledge
from right import Ui_right
from mistake import Ui_mistake
2、建立資料庫
#標識說明:0=可回收垃圾、1=廚余垃圾、2=有害垃圾、3=其他垃圾#
#比對庫
data = {"碎玻璃酒瓶":0,"蘋果核":1,"易拉罐":0,"塑料瓶":0,"香蕉皮":1,"雞蛋殼":1,"污損垃圾袋":2,
"廢紙":0,"一次性塑料杯":2,"螺絲刀":0,"舊衣服":3,"紙尿褲":3,"煙頭":3,"大骨棒":3,
"電池":2,"過期藥品":2,"節能燈":2,"水銀溫度計":2,"殺蟲劑":2,"灰土":3}
#題庫
datas = ["水銀溫度計","碎玻璃酒瓶","紙尿褲", "蘋果核", "易拉罐", "塑料瓶", "污損垃圾袋",
"殺蟲劑", "一次性塑料杯", "螺絲刀", "舊衣服","廢紙", "煙頭", "大骨棒",
"電池","雞蛋殼", "過期藥品","香蕉皮" "節能燈", "灰土"]
3、創建新類并寫入功能(部分注釋在后續代碼中不做解釋)
#開始頁
#界面
class MainWindow(QDialog,Ui_welcome):#繼承
def __init__(self):#初始化
super(MainWindow,self).__init__()#實體化視窗
self.setupUi(self)#寫入UI界面檔案
self.join.clicked.connect(self.join_main)#將信號連接到自定義槽函式join_main
self.exit3.clicked.connect(lambda :self.close())#退出按鈕連接到close函式
#功能實作
def join_main(self):#定義槽函式
self.main = MainWindow1()
self.main.show()#打開主界面
self.close()#關倍訓迎頁
4、主界面和功能實作
#主界面
class MainWindow1(QMainWindow,Ui_Form):
def __init__(self):
super(MainWindow1,self).__init__()
self.setupUi(self)
self.index = 0
self.knowledge.clicked.connect(self.join_knowledge)#將信號連接到自定義槽函式join_knowledge
self.start.clicked.connect(self.start_game)#將信號連接到自定義槽函式start_game
#將各個按鈕連接到函式game
# 廚余 1
self.PB1.clicked.connect(lambda :self.game(1))
# 可回收 0
self.PB2.clicked.connect(lambda :self.game(0))
# 有害 2
self.PB3.clicked.connect(lambda :self.game(2))
# 其他 3
self.PB4.clicked.connect(lambda :self.game(3))
#游戲功能實作
def game(self,index):
global data,datas#宣告資料庫,以便于在函式內使用
ans = self.lineEdit.text() #將文本框里面的內容賦值給ans 方便下面判斷對錯
#正確 彈出正確視窗
if index == data[ans]:#與資料庫中對應鍵值對進行比對(自定義字典)
self.main1 = MainWindow3()
self.main1.a.connect(self.next)#將信號源連接到下一題 next
self.main1.show()
else:
self.main1 = MainWindow4()#回答錯誤將不會切換下一題 僅僅彈出答錯界面
self.main1.show()
#下一題
def next(self,index):
global datas,data
self.index +=1 #自增
self.lineEdit.setText(datas[self.index]) #顯示下一題
def start_game(self):
global data,datas
self.lineEdit.setText(datas[0])#顯示題目
def join_knowledge(self):
self.main= MainWindow2()
self.main.show()
5、其他頁面
#知識界面
class MainWindow2(QDialog,Ui_knowledge):
def __init__(self):
super(MainWindow2,self).__init__()
self.setupUi(self)
#正確界面
class MainWindow3(QDialog,Ui_right):
a = pyqtSignal(str)
def __init__(self):
super(MainWindow3,self).__init__()
self.setupUi(self)
self.exit2.clicked.connect(self.chuan)
def chuan(self):
self.a.emit("正確")
self.close()
#錯誤界面
class MainWindow4(QDialog,Ui_mistake):
def __init__(self):
super(MainWindow4,self).__init__()
self.setupUi(self)
self.exit1.clicked.connect(self.chuan)
def chuan(self):
self.close()
6、主函式
#固定寫法,主函式
if __name__ == "__main__":
app = QApplication(sys.argv)#創建APP
window = MainWindow()#定義視窗
window.show()#使用show方法
sys.exit(app.exec_())##回圈等待退出
文中還有很多細節沒有寫出來,尤其是功能實作那一塊,鑒于筆者剛開始學習,計劃整理一下思路,后面再出一篇文章來細講功能實作,這里就不浪費篇幅了,
到此,我們的程式開發已經進行了絕大部分,后面就是不斷的除錯優化,這里就不講程序了
給大家講講踩坑日記
踩坑日記二
前面講到,一定要對控制元件的和槽函式的命名有足夠的耐心,因為在筆者撰寫代碼時,并沒有出現一大堆同名的按鈕控制元件和函式名,使得撰寫還比較順利,但是可以想象,如果大家都長得一樣,是很容易出錯,并且還不容易找到錯誤的,這里講一個例子
self.knowledge.clicked.connect(self.join_knowledge)
def join_knowledge(self):
self.main= MainWindow2()
self.main.show()
這個槽函式將打開知識界面,但是在筆者第一次撰寫時,函式的命名和按鈕控制元件的名字一樣,本以為可以運行,但是卻意外報錯,后將函式名進行了修改后就可以正常運行,這里我也不知道是什么原因,但是還是給各位提醒一下,有大佬知道的話可以在評論區告知我,
踩坑日記三
上文講到,QT中的資源檔案使用并不方便,在使用時不僅需要轉化ui檔案,還需要轉化qrc檔案,在后面打包成exe檔案時,還有更多麻煩,所以在這里大家可以看到,筆者都是使用的圖片的絕對路徑來進行呼叫的,可以給大家作為參考,
踩坑日記四
上文還講到,ui檔案轉化為py的問題,這里在啰嗦兩句,如果你采用的是跟筆者一樣的方式
pyuic5 -o try.py right.ui
轉化出來的代碼就會是這樣的
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_right(object):
def setupUi(self, right):
#############
#相同部分省略#
#############
如果你采用的是
pyuic5 -o try.py right.ui -x
轉化出來的代碼就會是這樣的
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_right(object):
def setupUi(self, right):
#############
#相同部分省略#
#############
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
right = QtWidgets.QDialog()
ui = Ui_right()
ui.setupUi(right)
right.show()
sys.exit(app.exec_())
兩者在UI部分是沒有差別的,差別在于-x為了使得轉化出來的檔案可以直接運行,是給檔案自動加上了主函式的,這就是界面邏輯統一的思想,但是我們這次采用的是分離的思想,所以我么不能用這種方式,不然main.py在呼叫時會報錯,
程式封裝
我們的代碼寫完后,可以運行,是因為我們電腦上有程式運行所需要的環境和各種庫,但是我們寫出來的程式不能只在我們電腦上運行,那其他人電腦上不可能都安裝了python和各種庫,也不可能讓他們先安裝在運行,這樣就失去了程式的意義,所以我們要將程式打包成一個exe檔案,這樣在其它電腦上就可以直接打開運行了,所以接下來我們介紹打包封裝,
打包我們同樣用到了一個模塊:pyinstaller 可以采用pip命令進行安裝,不多贅述
打包前,我們需要將所有的原始碼,圖片放在同一個檔案夾,在當前檔案夾下運行cmd命令
pyinstaller -F -w -i logo.ico main.py
-
-F是,表示要生成一整個檔案包含所有用到的東西(筆者選擇這樣,因為專案比較小,不會有很大的包,但是如果專案比較大不建議這么做,因為生成的檔案太大了,可以選擇-D 部署為一個檔案夾)
-
-w 使用Windows子系統執行,當程式啟動的時候不會打開命令列(只對Windows有效)
-
-i 將目錄下的ico檔案設定為exe圖示(這里注意一定要是ico格式的圖片,這里給大家介紹一個網站可以轉化為ico,灰常好用,見下面)(-o后面就是ico檔案的名字)
-
最后就是將要生成的exe檔案的py檔案名(如果你的專案是邏輯潔敏分離,并且在main.py中呼叫其他檔案的,你可以和我一樣操作,只將這個main.py打包就行了,他會幫你把其他的檔案也包含進去的)
http://www.ico51.cn/
執行操作后將會在目錄下生成兩個檔案夾,其中dist檔案夾中的exe檔案就是我們最后生成的可執行檔案啦,


最終效果
垃圾分類小程式
后記
到這,基本上就完成了整個專案,其實說起來好像很簡單,但是還是包含了很多辛酸淚的,筆者作為一個初學者,這段時間幾乎是住在了CSDN和Github,不斷地查資料,嘗試,失敗,推到重來,但是好在沒有選擇放棄,可能這個專案對于大佬來說很容易,幾個小時就能寫完,但是筆者既然選擇寫下來就是為了鼓勵自己不斷地學習,希望有朝一日能夠成為大佬hhhhh,
在這里還有幾局話想對想轉行做程式員的同學說的,筆者之前是學通信的,說實話編程基礎一般,資料庫、資料結構都沒有學過,轉行過來也才發現隔行如隔山,所以真的需要足夠的興趣和大量的學習、練習,做一名程式猿,還要能要坐得住,這是我認為最重要的,基礎,編程語言什么的都可以靠學習,但是耐心只能靠自己,
第一次做專案,第一次寫博客,文筆有限,諸君海涵,
路漫漫其修遠兮,吾將上下而求索,
(另外全部源檔案,有需要的可以私聊我XX )
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/289867.html
標籤:python
