1.下載安裝
(1)Pycharm:下載鏈接
(2)推薦使用Qt Designer來設計界面,如果你裝的是Anaconda的話,就已經自帶了designer.exe,我這里使用的是Pycharm的虛擬環境,通過以下的方式安裝:
(venv) G:\GitWorkspace\PycharmProjects\PythonStudy>pip install pyqt5
Collecting pyqt5
Downloading https://files.pythonhosted.org/packages/b2/87/219cd547c0deb902edd3fdbf1af01409f7f9d251344b9154d878203cf841/PyQt5-5.12.1-5.12.2-cp35.cp36.cp37.cp38-none-win_amd64.whl (47.4MB)
100% |████████████████████████████████| 47.4MB 40kB/s
Collecting PyQt5_sip<4.20,>=4.19.14 (from pyqt5)
Downloading https://files.pythonhosted.org/packages/ee/03/a11da2a5dd75b0ccd795ddbd944c18ce663ffb08d59a1e7f1ee5a597488c/PyQt5_sip-4.19.15-cp36-none-win_amd64.whl (51kB)
100% |████████████████████████████████| 61kB 35kB/s
Installing collected packages: PyQt5-sip, pyqt5
Successfully installed PyQt5-sip-4.19.15 pyqt5-5.12.1
(venv) G:\GitWorkspace\PycharmProjects\PythonStudy>pip install pyqt5-tools
Collecting pyqt5-tools
Downloading https://files.pythonhosted.org/packages/21/ff/7578d6d124926ff6cd1ef13c8c095063ad4c3811f4506788a915607aee81/pyqt5_tools-5.11.3.1.4-cp37-none-win32.whl (47.9MB)
100% |████████████████████████████████| 47.9MB 275kB/s
Collecting click (from pyqt5-tools)
Downloading https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl (81kB)
100% |████████████████████████████████| 81kB 836kB/s
Collecting pyqt5==5.11.3 (from pyqt5-tools)
Downloading https://files.pythonhosted.org/packages/26/78/c215008e70982944272b6c329a76cc11259e7378a3e23418fbe0bd48de96/PyQt5-5.11.3-5.11.2-cp35.cp36.cp37.cp38-none-win32.whl (39.4MB)
100% |████████████████████████████████| 39.4MB 206kB/s
Collecting python-dotenv (from pyqt5-tools)
Downloading https://files.pythonhosted.org/packages/8c/14/501508b016e7b1ad0eb91bba581e66ad9bfc7c66fcacbb580eaf9bc38458/python_dotenv-0.10.1-py2.py3-none-any.whl
Requirement already satisfied: PyQt5_sip<4.20,>=4.19.11 in c:\mzhu_working1023\python-workspace\study\venv\lib\site-packages (from pyqt5==5.11.3->pyqt5-tools) (4.19.15)
Installing collected packages: click, pyqt5, python-dotenv, pyqt5-tools
Found existing installation: PyQt5 5.12.1
Uninstalling PyQt5-5.12.1:
Successfully uninstalled PyQt5-5.12.1
Successfully installed click-7.0 pyqt5-5.11.3 pyqt5-tools-5.11.3.1.4 python-dotenv-0.10.1
2.環境配置
在Pycharm中,點擊File->Settings->Tools->External Tools點擊+添加以下工具:
(1)qtdesinger

(2)pyuic5,這個是把qt的UI文件轉換成.py文件的工具

(3)pyrcc, 這個是將資源文件如圖片等轉成python代碼能識別的文件,這個參數基本和pyuic5的是一樣的

這樣就基本完成PYQT5在Pycharm環境中的配置:

3.測試
(這里拿一個OpenCV的小練習來測試一下------)
打開qtdesinger,新建一個窗體:

界面的左側是Qt的常用控件”Widget Box”,右側有一個控件屬性窗口”Property Editor”,其余暫時用不到。本例中我們只用到了”Push Button”控件和”Label”控件:最上面的三個Label控件用於顯示圖片,可以在屬性窗口調整它的大小,我們統一調整到150×150:

另外,控件上顯示的文字”text”屬性和控件的名字”objectName”屬性需要修改,便於顯示和代碼調用:

點擊Designer工具欄的”Edit Signals/Slots”按鈕,進入槽函數編輯界面,點擊旁邊的”Edit Widgets”可以恢復正常視圖:

然后點擊按鈕並拖動,當產生類似於電路中的接地符號時釋放鼠標,在彈出的配置窗口中,可以看到左側是按鈕的常用事件,我們選擇點擊事件”clicked()”,然后添加一個名為”btnOpenCamera_Clicked()”的槽函數:

重復上面的步驟,給五個按鈕添加五個槽函數,最終結果如下:

到此,我們就完成了界面設計的所有工作,按下Ctrl+S保存當前窗口為.ui文件。.ui文件其實是按照XML格式標記的內容,可以用文本編輯器將.ui文件打開看看。
UI文件轉py代碼
因為我們是用Designer工具設計出的界面,並不是用Python代碼敲出來的,所以要想真正運行,需要使用pyuic5將ui文件轉成py文件。pyuic5.exe默認在%\Scripts\下,比如我的是在:G:\GitWorkspace\PycharmProjects\PythonStudy\venv\Scripts\
打開cmd命令行,切換到ui文件的保存目錄。Windows下有個小技巧,可以在目錄的地址欄輸入cmd,一步切換到當前目錄. 然后執行這條指令:
pyuic5 -o mainForm.py pyqt_create_ui.ui

執行正常的話,就會生成mainForm.py文件,里面應該包含一個名為”Ui_MainWindow”的類。
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'pyqt_create_ui.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(859, 474)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.btnOpenCamera = QtWidgets.QPushButton(self.centralwidget)
self.btnOpenCamera.setGeometry(QtCore.QRect(60, 370, 100, 30))
self.btnOpenCamera.setObjectName("btnOpenCamera")
self.btnCaptureImg = QtWidgets.QPushButton(self.centralwidget)
self.btnCaptureImg.setGeometry(QtCore.QRect(220, 370, 100, 30))
self.btnCaptureImg.setObjectName("btnCaptureImg")
self.btnOpenImg = QtWidgets.QPushButton(self.centralwidget)
self.btnOpenImg.setGeometry(QtCore.QRect(370, 370, 93, 28))
self.btnOpenImg.setObjectName("btnOpenImg")
self.btnGraying = QtWidgets.QPushButton(self.centralwidget)
self.btnGraying.setGeometry(QtCore.QRect(520, 370, 100, 30))
self.btnGraying.setObjectName("btnGraying")
self.btnThresholdSegment = QtWidgets.QPushButton(self.centralwidget)
self.btnThresholdSegment.setGeometry(QtCore.QRect(670, 370, 150, 30))
self.btnThresholdSegment.setObjectName("btnThresholdSegment")
self.labelCamera = QtWidgets.QLabel(self.centralwidget)
self.labelCamera.setGeometry(QtCore.QRect(70, 100, 150, 150))
self.labelCamera.setObjectName("labelCamera")
self.labelCapture = QtWidgets.QLabel(self.centralwidget)
self.labelCapture.setGeometry(QtCore.QRect(330, 100, 150, 150))
self.labelCapture.setObjectName("labelCapture")
self.labelResult = QtWidgets.QLabel(self.centralwidget)
self.labelResult.setGeometry(QtCore.QRect(630, 100, 150, 150))
self.labelResult.setObjectName("labelResult")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 859, 26))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.btnOpenCamera.clicked.connect(MainWindow.btnOpenCamera_Clicked)
self.btnCaptureImg.clicked.connect(MainWindow.btnCaptureImg_Clicked)
self.btnOpenImg.clicked.connect(MainWindow.btnOpenImg_Clicked)
self.btnGraying.clicked.connect(MainWindow.btnGrayingImg_Clicked)
self.btnThresholdSegment.clicked.connect(MainWindow.btnThresholdSegment_Clicked)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.btnOpenCamera.setText(_translate("MainWindow", "Open Camera"))
self.btnCaptureImg.setText(_translate("MainWindow", "Capture Image"))
self.btnOpenImg.setText(_translate("MainWindow", "Open Image"))
self.btnGraying.setText(_translate("MainWindow", "Graying"))
self.btnThresholdSegment.setText(_translate("MainWindow", "Threshold Segmentation"))
self.labelCamera.setText(_translate("MainWindow", "Camera"))
self.labelCapture.setText(_translate("MainWindow", "Captured Image"))
self.labelResult.setText(_translate("MainWindow", "Result Image"))
[注:mainForm.py文件是根據ui文件生成的,也就是說重新生成會覆蓋掉。所以為了使界面與邏輯分離,我們需要新建一個邏輯文件。]
在同一工作目錄下新建一個”mainEntry.py”的文件,存放邏輯代碼。
import sys
import cv2
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import QFileDialog, QMainWindow
from mainForm import Ui_MainWindow
class PyQtMainEntry(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.camera = cv2.VideoCapture(1) # 0-調用筆記本內置攝像頭,1-調用usb攝像頭
self.is_camera_opened = False # 攝像頭有沒有打開標記
# 定時器:30ms捕獲一幀
self._timer = QtCore.QTimer(self)
self._timer.timeout.connect(self._queryFrame)
self._timer.setInterval(30)
def btnOpenCamera_Clicked(self):
'''
打開和關閉攝像頭
'''
self.is_camera_opened = ~self.is_camera_opened
if self.is_camera_opened:
self.btnOpenCamera.setText("Close Camera")
self._timer.start()
else:
self.btnOpenCamera.setText("Open Camera")
self._timer.stop()
def btnCaptureImg_Clicked(self):
'''
捕獲圖片
'''
# 攝像頭未打開,不執行任何操作
if not self.is_camera_opened:
return
self.captured = self.frame
# 后面這幾行代碼幾乎都一樣,可以嘗試封裝成一個函數
rows, cols, channels = self.captured.shape
bytesPerLine = channels * cols
# Qt顯示圖片時,需要先轉換成QImgage類型
QImg = QImage(self.captured.data, cols, rows,
bytesPerLine, QImage.Format_RGB888)
self.labelCapture.setPixmap(QPixmap.fromImage(QImg).scaled(
self.labelCapture.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
def btnOpenImg_Clicked(self):
'''
從本地讀取圖片
'''
# 打開文件選取對話框
filename, _ = QFileDialog.getOpenFileName(self, '打開圖片')
if filename:
self.captured = cv2.imread(str(filename))
# OpenCV圖像以BGR通道存儲,顯示時需要從BGR轉到RGB
self.captured = cv2.cvtColor(self.captured, cv2.COLOR_BGR2RGB)
rows, cols, channels = self.captured.shape
bytesPerLine = channels * cols
QImg = QImage(self.captured.data, cols, rows,
bytesPerLine, QImage.Format_RGB888)
self.labelCapture.setPixmap(QPixmap.fromImage(QImg).scaled(
self.labelCapture.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
def btnGrayingImg_Clicked(self):
'''
灰度化
'''
# 如果沒有捕獲圖片,則不執行操作
if not hasattr(self, "captured"):
return
self.cpatured = cv2.cvtColor(self.captured, cv2.COLOR_RGB2GRAY)
rows, columns = self.cpatured.shape
bytesPerLine = columns
# 灰度圖是單通道,所以需要用Format_Indexed8
QImg = QImage(self.cpatured.data, columns, rows,
bytesPerLine, QImage.Format_Indexed8)
self.labelResult.setPixmap(QPixmap.fromImage(QImg).scaled(
self.labelResult.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
def btnThresholdSegment_Clicked(self):
'''
Otsu自動閾值分割
'''
if not hasattr(self, "captured"):
return
_, self.cpatured = cv2.threshold(
self.cpatured, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
rows, columns = self.cpatured.shape
bytesPerLine = columns
# 閾值分割圖也是單通道,也需要用Format_Indexed8
QImg = QImage(self.cpatured.data, columns, rows,
bytesPerLine, QImage.Format_Indexed8)
self.labelResult.setPixmap(QPixmap.fromImage(QImg).scaled(
self.labelResult.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
@QtCore.pyqtSlot()
def _queryFrame(self):
'''
循環捕獲圖片
'''
ret, self.frame = self.camera.read()
img_rows, img_cols, channels = self.frame.shape
bytesPerLine = channels * img_cols
cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB, self.frame)
QImg = QImage(self.frame.data, img_cols, img_rows,
bytesPerLine, QImage.Format_RGB888)
self.labelCamera.setPixmap(QPixmap.fromImage(QImg).scaled(
self.labelCamera.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = PyQtMainEntry()
window.show()
sys.exit(app.exec_())
運行結果:

參考文章:
http://ex2tron.wang/opencv-python-using-pyqt5-create-gui/#%E6%8C%91%E6%88%98%E5%86%85%E5%AE%B9
