changed 4 years ago
Linked with GitHub
tags: python

PyQt 實作

環境安裝

pip install PyQt5 pip install pyqt5-tools pip install youtube_dl

Qt Designer

VSCode PyQt5擴充套件

  1. 按Ctrl + Shift + X
  2. 在搜尋框搜尋「Qt for Python」
  3. 點安裝

執行pyqt designer

C:\Users\你的電腦帳號\AppData\Local\Programs\Python\Python36-32\Scripts\designer.exe

範例程式

flyingfish.ui (完全透過pyqt designer產生)

  1. 建立功能烈表:檔案/結束
  2. 建立上圖元件
  3. 元件命名,方便python指定

  1. 事件綁定
    當元件指定事件發生時呼叫指定函數

最終事件綁定

儲存附檔名為.ui的檔案,內容如下

<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>FlyingFish</class> <widget class="QMainWindow" name="FlyingFish"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>158</height> </rect> </property> <property name="minimumSize"> <size> <width>800</width> <height>158</height> </size> </property> <property name="maximumSize"> <size> <width>800</width> <height>174</height> </size> </property> <property name="font"> <font> <pointsize>14</pointsize> </font> </property> <property name="windowTitle"> <string>FlyingFish 1.0</string> </property> <property name="windowIcon"> <iconset> <normaloff>flyingfish.ico</normaloff>flyingfish.ico</iconset> </property> <widget class="QWidget" name="centralwidget"> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> <widget class="QLabel" name="label_youtube_url"> <property name="font"> <font> <pointsize>16</pointsize> </font> </property> <property name="text"> <string>Youtube影片網址</string> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label_file_path"> <property name="font"> <font> <pointsize>16</pointsize> </font> </property> <property name="text"> <string>影片儲存位置</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QLineEdit" name="input_download_file_path"> <property name="font"> <font> <pointsize>16</pointsize> </font> </property> <property name="toolTip"> <string>請點瀏覽選擇影片要儲存到哪個資料夾</string> </property> </widget> </item> <item row="1" column="2"> <widget class="QPushButton" name="button_browser"> <property name="font"> <font> <pointsize>14</pointsize> </font> </property> <property name="text"> <string>瀏覽</string> </property> <property name="autoDefault"> <bool>true</bool> </property> <property name="default"> <bool>false</bool> </property> </widget> </item> <item row="0" column="1" colspan="2"> <widget class="QLineEdit" name="input_youtube_url"> <property name="font"> <font> <pointsize>16</pointsize> </font> </property> <property name="toolTip"> <string>請貼上youtube要下載影片的網址</string> </property> </widget> </item> <item row="2" column="1" colspan="2"> <widget class="QPushButton" name="button_download"> <property name="font"> <font> <pointsize>14</pointsize> </font> </property> <property name="text"> <string>開始下載</string> </property> <property name="autoDefault"> <bool>true</bool> </property> <property name="default"> <bool>false</bool> </property> </widget> </item> </layout> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>30</height> </rect> </property> <property name="font"> <font> <pointsize>14</pointsize> </font> </property> <widget class="QMenu" name="menu"> <property name="font"> <font> <pointsize>14</pointsize> </font> </property> <property name="title"> <string>檔案</string> </property> <addaction name="menu_exit"/> </widget> <addaction name="menu"/> </widget> <widget class="QStatusBar" name="statusbar"> <property name="font"> <font> <pointsize>14</pointsize> </font> </property> </widget> <action name="menu_exit"> <property name="text"> <string>結束</string> </property> <property name="font"> <font> <pointsize>14</pointsize> </font> </property> <property name="shortcut"> <string>Ctrl+Q</string> </property> </action> </widget> <tabstops> <tabstop>input_youtube_url</tabstop> <tabstop>input_download_file_path</tabstop> <tabstop>button_browser</tabstop> <tabstop>button_download</tabstop> </tabstops> <resources/> <connections> <connection> <sender>button_download</sender> <signal>clicked()</signal> <receiver>FlyingFish</receiver> <slot>download_youtube_video()</slot> <hints> <hint type="sourcelabel"> <x>478</x> <y>114</y> </hint> <hint type="destinationlabel"> <x>399</x> <y>78</y> </hint> </hints> </connection> <connection> <sender>menu_exit</sender> <signal>triggered()</signal> <receiver>FlyingFish</receiver> <slot>close()</slot> <hints> <hint type="sourcelabel"> <x>-1</x> <y>-1</y> </hint> <hint type="destinationlabel"> <x>399</x> <y>78</y> </hint> </hints> </connection> <connection> <sender>button_browser</sender> <signal>clicked()</signal> <receiver>FlyingFish</receiver> <slot>setSaveFilePath()</slot> <hints> <hint type="sourcelabel"> <x>753</x> <y>75</y> </hint> <hint type="destinationlabel"> <x>399</x> <y>78</y> </hint> </hints> </connection> </connections> <slots> <slot>download_youtube_video()</slot> <slot>setSaveFilePath()</slot> </slots> </ui>
  1. 打開VS code
    flyingfish.py

a. 載入pyqt套件

from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog from PyQt5 import uic

b. 載入designer設計好的ui檔

form_class = uic.loadUiType("youtube_dl.ui")[0]

c. 呈現UI介面、狀態列訊息

def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.status = self.statusBar() self.status.showMessage("狀態: 等待使用者") # 設定狀態訊息

d. 設定檔案儲存路徑

def setSaveFilePath(self): options = QFileDialog.Options() folderName = QFileDialog.getExistingDirectory(self, "Open a folder", "", QFileDialog.ShowDirsOnly) # 開啟資料夾選取視窗 self.input_download_file_path.setText(folderName)

e. 儲存youtube音樂檔、影片檔

def download_youtube_video(self, MainWindow): self.status.showMessage("狀態: 開始下載...") download_path = os.path.join(self.input_download_file_path.text(), '%(title)s.%(ext)s') # 設定影片儲存路徑 ydl = YoutubeDL({'outtmpl': download_path}) # 呼叫youtube-dl來下載影片 with ydl: result = ydl.extract_info(self.input_youtube_url.text(), download=True) self.status.showMessage("狀態: 影片下載完成! ")

完整python檔案

import sys, os from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog from PyQt5 import uic from youtube_dl import YoutubeDL form_class = uic.loadUiType("youtube_dl.ui")[0] # 載入PyQt Designer設計好的畫面 class MainWindow(QMainWindow, form_class): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.status = self.statusBar() self.status.showMessage("狀態: 等待使用者") # 設定狀態訊息 def setSaveFilePath(self): options = QFileDialog.Options() folderName = QFileDialog.getExistingDirectory(self, "Open a folder", "", QFileDialog.ShowDirsOnly) # 開啟資料夾選取視窗 self.input_download_file_path.setText(folderName) def download_youtube_video(self, MainWindow): self.status.showMessage("狀態: 開始下載...") download_path = os.path.join(self.input_download_file_path.text(), '%(title)s.%(ext)s') # 設定影片儲存路徑 ydl = YoutubeDL({'outtmpl': download_path}) # 呼叫youtube-dl來下載影片 with ydl: result = ydl.extract_info(self.input_youtube_url.text(), download=True) self.status.showMessage("狀態: 影片下載完成! ") if __name__ == "__main__": app = QApplication(sys.argv) Win = MainWindow() Win.show() sys.exit(app.exec_())

打包成exe及壓縮成zip

  1. 安裝pyinstaller
pip install pyinstaller
  1. 請準備ico格式圖檔
  2. 打包成exe
pyinstaller -F -w -i=flyingfish.ico flyingfish.py
  1. 將相關資源檔複製到dist資料夾中
    a. ico圖檔
    b. ui介面檔
  2. 執行dist中產生的exe檔,測試是否所有功能都正常
  3. 將dist壓縮成zip

完成檔

flyingfish.zip

異常排除

Q: 程式圖示無法更換成指定ico檔

  1. 刪除C:\Users<使用者帳號>\AppData\Local\Microsoft\Windows\Explorer的檔案(刪除圖示快取!)
  2. 重新執行打包exe

notepad範例

import sys, os from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog from PyQt5 import uic form_class = uic.loadUiType("notepad.ui")[0] # 載入PyQt Designer設計好的畫面 class MainWindow(QMainWindow, form_class): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.status = self.statusBar() self.status.showMessage("狀態: 等待使用者") # 設定狀態訊息 def savefile(self): text = self.textEdit.toPlainText() options = QFileDialog.Options() path, _ = QFileDialog.getSaveFileName(self, "Save file", "", "Text documents (*.txt);;All files (*.*)") with open(path, 'w') as f: f.write(text) def openfile(self): path, _ = QFileDialog.getOpenFileName(self, "Open file", "", "Text documents (*.txt);;All files (*.*)") with open(path, 'r') as f: text = f.read() self.textEdit.setPlainText(text) if __name__ == "__main__": app = QApplication(sys.argv) Win = MainWindow() Win.show() sys.exit(app.exec_())

參考資料

Select a repo