面向聰明小白的程式設計入門教程 照貓畫貓(Part 3)
程式設計是一種自我挑戰。挑戰需要的是激情,激情需要的是能看到點滴的進步。大學四年,沒學會怎麼做一個能顯示“Hello World”的空窗體出來,日復一日地在命令列下研究演算法、資料結構、作業系統、編譯原理,到頭來還是一臉蒙逼。獨木舟還沒造出來,就去研究航母的核子發動機,連紙上談兵的趙括都笑了。
我們的第一課,就是做一個有具體功能的,有一定實用性的Windows窗體應用程式出來,並且可以打包成.exe
可執行檔案給別人使用。有時候,囫圇吞棗,也不一定是貶義詞。
這個要做的軟體,我們稱之為“麥多文件轉換器”。具體的介紹、截圖和原始碼見Github專案主頁 。Github是全球最大的開源軟體集散中心。
此軟體是我在業餘時間,花了七八個小時開發而成。之所以要花這麼長時間,是因為好多東西不熟或者忘了,我也是現學現賣。
1. 原始碼窺探
import os import os.path import platform from subprocess import Popen, PIPE from threading import Thread from PyQt5.QtCore import * from PyQt5.QtWidgets import * label: QLabel wnd: QMainWindow currentFilename: str = None dlg: QProgressDialog = None def loadFile(filename): if filename is None: return if platform.system() == "Windows": filename = filename[1:] global currentFilename currentFilename = filename label.setText("當前檔案: " + filename) wnd.statusBar().showMessage("檔案載入成功: " + filename) class Converter(QThread): done = pyqtSignal(bool) def __init__(self, fmt): super().__init__() self.fmt = fmt def run(self): fmt = self.fmt fromFilename = currentFilename toFilename = os.path.splitext(fromFilename)[0] + "." + fmt args = ["pandoc", fromFilename] if fmt == "pdf": args.extend(["--pdf-engine", "wkhtmltopdf"]) args.extend(["-o", toFilename]) try: from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW startupinfo = STARTUPINFO() startupinfo.dwFlags |= STARTF_USESHOWWINDOW except: startupinfo = None process = Popen(args=args, stdin=PIPE, stdout=PIPE, stderr=PIPE, startupinfo=startupinfo) err = process.communicate()[1].decode() code = process.returncode wnd.statusBar().showMessage(("轉換成功: " + toFilename) if code == 0 else "轉換失敗: " + err) self.done.emit(True) def convertFile(fmt): if currentFilename is None: return global dlg dlg = QProgressDialog(wnd) dlg.setWindowTitle("轉換中...") dlg.setMaximum(0) dlg.setCancelButton(None) dlg.setWindowFlags(Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint) dlg.setWindowModality(Qt.WindowModal) dlg.show() wnd.statusBar().showMessage("轉換中...") global converter converter = Converter(fmt) converter.done.connect(dlg.close) converter.start() app = QApplication([]) wnd = QMainWindow() wnd.resize(400, 400 * 0.618) wnd.setAcceptDrops(True) wnd.dragEnterEvent = lambda e: e.accept() wnd.dropEvent = lambda e: print(e.mimeData().urls()) wnd.dropEvent = lambda e: loadFile(next(iter([x.path() for x in e.mimeData().urls()]), None)) wnd.statusBar().showMessage("就緒.") payload = QWidget() label = QLabel("請拖入檔案.轉換後會自動覆蓋同名檔案,請謹慎使用") buttons = QGridLayout() mapper = QSignalMapper() for idx, (k, v) in enumerate(dict(docx="Word", pdf="PDF", pptx="PowerPoint", html="HTML", mobi="Mobi", epub="EPUB").items()): button = QPushButton(v) button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) mapper.setMapping(button, k) button.clicked.connect(mapper.map) buttons.addWidget(button, idx / 3 + 1, idx % 3 + 1) mapper.mapped["QString"].connect(convertFile) layout = QVBoxLayout() layout.addWidget(label) layout.addLayout(buttons) payload.setLayout(layout) wnd.setCentralWidget(payload) wnd.setWindowTitle("麥多文件轉換器") wnd.show() font = app.font() font.setPointSize(font.pointSize() / 0.618) app.setFont(font) app.exec()
一共區區104行程式碼,就完成了整個軟體需要的全部功能。可見,程式設計並沒有想象中的那麼複雜,至少,不需要想象中那樣多的程式碼。
2. 下載原始碼
原始碼的下載連結
。由於原始碼是個文字檔案,瀏覽器應該不會彈出下載提示框,需要我們按下Ctrl+S
,將此檔案儲存到本地。儲存位置放到桌面。儲存型別選擇所有檔案(*.*)
。
下載完成後,桌面上會出現一個Python(一種程式語言)原始碼檔案app.py
,請確保它的名字不是app.py.txt
。
滑鼠右鍵點選這個app.py
,選擇“用記事本開啟”,即可檢視原始碼。如果沒有此右鍵選單,開啟記事本,將檔案拖拽進記事本視窗即可。
3. 選擇文字編輯器
程式程式碼可以用記事本編寫,但是記事本不顯示程式碼行號,也沒有程式碼高亮,更沒有程式碼智慧提示等高階功能。所以,一般情況很少用記事本做開發(軟體開發,即程式設計)。
除了記事本,很多文字編輯器都支援程式碼高亮,比如來自灣灣的Notepad++
和來自微軟的Visual Studio Code
。Notepad++
主打小巧快捷方便,Visual Studio Code
主打功能豐富,外掛眾多,開箱即用,迷你IDE。建議兩者都安裝上,進行比較選擇。
這兩個編輯器的安裝方法:
choco install notepadplusplus choco install vscode
安裝完成後,可以將程式碼檔案app.py
分別拖進Notepad++
和Visual Studio Code
,看哪個更順眼就選用哪個。注意,以後的編碼並不會用這兩個軟體,而是要用真·宇宙第一的IDE:PyCharm。當然,這是後話了。
4. 安裝Python
麥多文件轉換器是用Python
程式語言編寫的。Python是一個比較流行的高階程式語言,作者來自荷蘭。
Python有2和3兩個版本,而且他們之間的相容性非常差。現在,新版的Python3已經普及了,一般情況下是沒有必要再學Python2了。但是,在網上搜索相關資料的時候,要注意這個版本。
在命令列下安裝Python:choco install python
。這個過程可能會特別慢,此時可以穿牆重新執行命令,或者直接官網下載安裝包執行。
Python安裝完成後,在命令列下執行python
,驗證是否安裝成功。如果Chocolatey
提示Python已安裝成功,但是執行python
命令可能會報錯:”‘python’ 不是內部或外部命令,也不是可執行的程式或批處理檔案。”。注意,此時報錯才正常。因為當前的PATH
環境變數裡,沒有Python可執行檔案所在的目錄。雖然Chocolatey
已經將Python需要的PATH
環境變數改好了,但我們的虛擬終端cmd
還沒有意識到,它開始執行時載入的環境變數並不會自動更新。此時,在同一個終端(不同終端的環境相互獨立,修改一個終端的環境變數,不會影響到其他已經開啟的終端)下執行命令refreshenv
,即可重新整理當前環境,讓虛擬終端更新儲存的環境變數。
5. Python互動式環境
如果一切正常,此時執行命令,應該可以看到如下的Python互動(輸入一句執行一句)環境。
C:\Users\Administrator>python Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>>
輸入2 ** 10
,回車,應該能看到輸出為1024
。此處,**
是求冪運算子。可見,Python可以直接當一個命令列下的計算器使用。
>>> 2 ** 10 1024 >>>
再輸入exit()
,回車,即可退出Python控制檯。注意,括號要輸入英文半形,因為Python是一門國際化的程式語言,不可能遷就這邊14億的玻璃心。
C:\Users\Administrator>python Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> 2 ** 10 1024 >>> exit() C:\Users\Administrator>
6. 執行我們的Python程式
如果我們的Python程式安裝得完全正常的話,應該可以雙擊執行。但是,如果程式有異常,很可能會只有一個黑屏一閃而過,不知道發生了什麼。所以,我們需要換種執行方法。
在桌面上按住Shift
鍵右擊滑鼠,選擇在此處開啟命令視窗
,會開啟一個控制檯視窗,並且這個控制檯下的當前目錄(專業術語叫當前工作目錄)就是桌面(桌面也是Windows系統的一個資料夾)。確保當前命令提示符為C:\Users\Administrator\Desktop>
,這樣Python
才能找到我們要執行的檔案。
在這個命令列下鍵入python app.py
,回車,應該會報出如下異常(錯誤資訊):
C:\Users\Administrator\Desktop>python app.py Traceback (most recent call last): File "app.py", line 7, in <module> from PyQt5.QtCore import * ModuleNotFoundError: No module named 'PyQt5'
注意,異常不一定是壞事,異常有時候正是程式設計師所期望的。比如,以上的異常完全正常,因為我們的程式的執行環境還缺少一些必要的東西。
7. 關於模組
一個好的程式設計師,肯定擅長在合適的場景選擇使用合適的工具。我們要寫的程式是一個圖形介面程式,然而,Windows只對(也只能對)C語言和組合語言開放了圖形介面的相關介面。因此,一個Python程式是不能直接呼叫系統介面的。Python提供了Windows介面呼叫的相關模組,但是這些模組只能用於Windows作業系統。為了照顧蘋果使用者,我們需要讓我們的應用程式可以跨作業系統(跨平臺)執行。
理論上,我們可以分別呼叫Windows和macOS的底層API,寫一個通用的程式出來。但是,這個工作量太大,不是一般人更不是小白能Hold住的。因此,我們選用第三方開發的圖形介面模組來進行我們的介面開發。在這裡,我們選擇的是來自前諾基亞公司的PyQt5
模組。
除了圖形介面模組,Python中還有用於處理檔案、網路等功能的各種模組。除了官方的模組,還有第三方開發的模組(任何人都可以開發併發布Python模組 )。使用模組,可以遮蔽掉那些我們不關心的底層實現細節,快速開發我們需要的功能。
在Windows上,我們可以用Chocolatey
管理軟體包。在Python中,我們需要用PIP
來管理Python模組。PIP
不僅能安裝我們需要的模組,也能自動處理模組間的依賴關係。
在命令列下執行pip install PyQt5
,即可自動安裝我們需要的圖形模組PyQt5。同理於Chocolatey
,我們也可以使用命令pip search pyqt
搜尋相關的模組。
PyQt5
安裝好後,繼續執行命令python app.py
。此時,應該可以看到應用窗體。這個窗體已經做好了自適應佈局,可以隨意拉伸縮放。拖入一個Word文件,點選PDF
,將這個Word文件轉為PDF格式。此時,程式應該會奔潰,介面應該會消失。
這次的程式奔潰非常正常,因為我們的應用的執行環境還缺少一些必要的依賴。我們做的應用是一個文件轉格式應用。文件轉格式,是一個很有技術含量,同時又十分枯燥的任務。所以,我們選擇使用第三方的工具在後臺幫我們轉。
我選擇使用pandoc
來轉換文件格式。pandoc
轉HTML格式沒問題,但是轉PDF
的話,它本身並不會,需要依賴其他的轉PDF工具。我配置的是一個比較小巧的wkhtmltopdf
。pandoc
和wkhtmltopdf
都可以通過Chocolatey
安裝:
choco install pandoc choco install wkhtmltopdf
安裝完成後,再執行文件轉換,應該會提示轉換成功,並在當前目錄下生成轉換好的檔案。
8. 自用開發好的軟體
我們開發好的軟體,不可能每次使用的時候都到命令列下打命令吧。好在正常安裝了Python之後,.py
檔案就是可執行檔案了,會自動呼叫Python來執行我們的程式。但是,直接執行.py
檔案,會帶出一個控制檯視窗,用以顯示程式日誌。這個控制檯視窗非常扎眼,將.py
檔案重新命名為.pyw
(w表示Window)後,雙擊.pyw
檔案,就不會再拖出這個命令列視窗了。
9. 打包開發好的軟體
獨樂樂不如眾樂樂。做好的程式能在我們電腦上執行,但是到別人電腦上是不能直接跑的。我們不能要求別人去安裝Python、安裝PyQt5、安裝pandoc、安裝wkhtmltopdf,尤其是這個別人是客戶的時候。所以,我們需要把程式程式碼和全部的依賴打包到一個可執行檔案裡面。這樣,只需要把這個.exe
傳送給別人,別人就可以直接雙擊運行了。
Python是一門指令碼語言,用它寫的程式不能直接執行,需要Python直譯器來一行程式碼一行程式碼地順序解釋執行。打包應用,至少要把Python直譯器打包到.exe
裡面。
Python官方不提供打包工具,因此,我們需要用到第三方的模組PyInstaller
。打命令pip install pyinstaller
安裝。這是一個可執行模組,可以直接執行。
PyInstaller安裝完成後,執行命令pyinstaller --onefile app.py
,即可將app.py
打包到dist\app.exe
。
此時,雙擊這個.exe
,程式正常執行,功能也沒問題。但是,這個軟體在別的電腦上還是沒法正常工作的。因為pandoc
和wkhtmltopdf
還沒有打包進去。不過,我也不清楚怎麼打包外部的.exe
,姑且先把pandoc.exe
和wkhtmltopdf.exe
放到app.exe
目錄下,將整個目錄打包成一個壓縮包吧。至少使用者解包後,執行app.exe
,還是能找到這兩個依賴的。
pandoc
和wkhtmltopdf
的安裝位置:
- C:\Users\Administrator\AppData\Local\Pandoc\pandoc.exe
- C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe