PyQt界面編程應(yīng)用與實(shí)踐
一個(gè)典型的GUI應(yīng)用程序可以抽象為:主界面(菜單欄、工具欄、狀態(tài)欄、內(nèi)容區(qū)域),二級(jí)界面(模態(tài)、非模態(tài)),信息提示(Tooltip),程序圖標(biāo)等組成。本篇根據(jù)作者使用PyQt5編寫(xiě)的一個(gè)工具,介紹如何使用PyQt5構(gòu)建一個(gè)典型的GUI應(yīng)用。
1. 主界面
QMainWindow類提供一個(gè)有菜單條、錨接窗口(例如工具條)和一個(gè)狀態(tài)條的主應(yīng)用程序窗口。主窗口通常用在提供一個(gè)大的中央窗口部件(例如文本編輯或者繪制畫(huà)布)以及周圍菜單、工具條和一個(gè)狀態(tài)條。QMainWindow常常被繼承,因?yàn)檫@使得封裝中央部件、菜單和工具條以及窗口狀態(tài)變得更容易。
菜單欄
創(chuàng)建菜單的代碼如下:
self.addMenu = self.menuBar().addMenu("&添加")
self.addMenu.addAction(self.addAvatarAct)
self.addMenu.addAction(self.addAvatarSetAct)
self.addMenu.addAction(self.addAvatarDecorationAct)
self.modifyMenu = self.menuBar().addMenu("&修改")
self.modifyMenu.addAction(self.modifyAvatarAct)
self.modifyMenu.addAction(self.modifyAvatarSetAct)
self.settingMenu = self.menuBar().addMenu("&設(shè)置")
self.settingMenu.addAction(self.settingAct)
其中每個(gè)菜單項(xiàng),關(guān)聯(lián)一個(gè)QAction,定義了圖標(biāo)、菜單名、回調(diào)函數(shù)、快捷鍵等等,這里沒(méi)有設(shè)置快捷鍵。
self.addAvatarAct = QAction(QIcon("res/ico/addAvatar.ico"), "&Add Avatar", self, triggered=self.addAvatar)
self.addAvatarSetAct = QAction(QIcon("res/ico/addAvatarSet.ico"), "&Add AvatarSet", self, triggered=self.addAvatarSet)
self.addAvatarDecorationAct = QAction(QIcon("res/ico/addAvatarDecoration.ico"), "&Add AvatarDecoration", self, triggered=self.addAvatarDecoration)
self.modifyAvatarAct = QAction(QIcon("res/ico/modifyAvatar.ico"), "&Modify Avatar or Decoration", self, triggered=self.modifyAvatar)
self.modifyAvatarSetAct = QAction(QIcon("res/ico/modifyAvatarSet.ico"), "&Modify AvatarSet", self, triggered=self.modifyAvatarSet)
self.settingAct = QAction(QIcon("res/ico/settingPath.ico"), "&路徑", self, triggered=self.settingPath)
self.homeAct = QAction(QIcon("res/ico/home.ico"), "&首頁(yè)", self, triggered=self.homePage)
說(shuō)明:QAction類提供了一個(gè)可以同時(shí)出現(xiàn)在菜單和工具條上的抽象用戶界面操作。
在圖形用戶界面應(yīng)用程序中很多命令可以通過(guò)菜單選項(xiàng)、工具條按鈕和鍵盤(pán)快捷鍵調(diào)用。因?yàn)橥粋€(gè)操作將會(huì)被執(zhí)行,而與它的調(diào)用方法無(wú)關(guān),并且因?yàn)椴藛魏凸ぞ邨l必須保持同步,所以提供一個(gè)操作這樣的命令很有用。一個(gè)操作可以被添加到菜單和工具條中并且將會(huì)自動(dòng)使它們同步。例如,如果用戶按下“加粗”工具條按鈕,“加粗”菜單項(xiàng)將會(huì)自動(dòng)被選中。
QAction可以包含圖標(biāo)、菜單文本、快捷鍵、狀態(tài)條文本、這是什么文本和工具提示。它們可以分別通過(guò)setIconSet()、setText()、setMenuText()、setToolTip()、setStatusTip()、setWhatsThis()和setAccel()來(lái)設(shè)置。
工具欄
創(chuàng)建工具欄的代碼如下:
self.toolbar = self.addToolBar('Home')
self.toolbar.addAction(self.homeAct)
self.toolbar = self.addToolBar('AddAvatar')
self.toolbar.addAction(self.addAvatarAct)
self.toolbar = self.addToolBar('AddAvatarDecoration')
self.toolbar.addAction(self.addAvatarDecorationAct)
self.toolbar = self.addToolBar('AddAvatarSet')
self.toolbar.addAction(self.addAvatarSetAct)
self.toolbar = self.addToolBar('ModifyAvatar')
self.toolbar.addAction(self.modifyAvatarAct)
self.toolbar = self.addToolBar('ModifyAvatarSet')
self.toolbar.addAction(self.modifyAvatarSetAct)
工具欄項(xiàng)也需要關(guān)聯(lián)一個(gè)QAction,可以和菜單項(xiàng)共用一個(gè)QAction,即一個(gè)QAction可以被關(guān)聯(lián)到多個(gè)地方。
狀態(tài)欄
設(shè)置狀態(tài)欄,只需要:
self.statusBar().showMessage("數(shù)據(jù)加載完成")
第一次調(diào)用self.statusBar()獲取工具欄時(shí),會(huì)初始化工具欄實(shí)例,后面再次調(diào)用不會(huì)在創(chuàng)建新的實(shí)例。
程序圖標(biāo)
程序圖標(biāo)分為2個(gè):程序窗口圖標(biāo);執(zhí)行文件的圖標(biāo)。
l setWindowIcon(QIcon(“res/ico/icon.ico”))設(shè)置程序窗口的圖標(biāo)
l 執(zhí)行文件的圖標(biāo),通過(guò)打包工具設(shè)置
2. UI布局
PyQt的布局系統(tǒng)提供了一個(gè)規(guī)定子窗口部件布局的簡(jiǎn)單的和強(qiáng)有力的方式。當(dāng)你一旦規(guī)定了合理的布局,你就會(huì)獲得如下利益:
l 布置子窗口部件。
l 最高層窗口部件可感知的默認(rèn)大小。
l 最高層窗口部件可感知的最小大小。
l 調(diào)整大小的處理。
l 當(dāng)內(nèi)容改變的時(shí)候自動(dòng)更新:
n 字體大小、文本或者子窗口部件的其它內(nèi)容。
n 隱藏或者顯示子窗口部件。
n 移去一些子窗口部件。
PyQt支持的布局方式有很多,如下表所示:
|
布局相關(guān)類 |
作用 |
|
QBoxLayout |
Lines up child widgets horizontally or vertically |
|
QButtonGroup |
Container to organize groups of button widgets |
|
QFormLayout |
Manages forms of input widgets and their associated labels |
|
QGraphicsAnchor |
Represents an anchor between two items in a QGraphicsAnchorLayout |
|
QGraphicsAnchorLayout |
Layout where one can anchor widgets together in Graphics View |
|
QGridLayout |
Lays out widgets in a grid |
|
QGroupBox |
Group box frame with a title |
|
QHBoxLayout |
Lines up widgets horizontally |
|
QLayout |
The base class of geometry managers |
|
QLayoutItem |
Abstract item that a QLayout manipulates |
|
QSizePolicy |
Layout attribute describing horizontal and vertical resizing policy |
|
QSpacerItem |
Blank space in a layout |
|
QStackedLayout |
Stack of widgets where only one widget is visible at a time |
|
QStackedWidget |
Stack of widgets where only one widget is visible at a time |
|
QVBoxLayout |
Lines up widgets vertically |
|
QWidgetItem |
Layout item that represents a widget |
其中使用比較多的是以下布局方式(或者說(shuō)是我使用比較多,不代表大家):
- 水平布局 QHBoxLayout
- 垂直布局 QVBoxLayout
- 網(wǎng)格布局 QGridLayout
水平布局
水平布局(QHBoxLayout)顧名思義,將空間水平切成多段,然后通過(guò)addWidget、addItem將widget填充指定的位置。如下代碼即實(shí)現(xiàn)了上圖中,適合角色選擇的水平布局:
hbox = QHBoxLayout()
self.roleChkBoxGroup.setLayout(hbox)
for _, v in sorted(ParseKeywords.profession.items()):
checkBox = QRadioButton(v["cname"] + " " + str(v["value"]))
hbox.addWidget(checkBox)
刪除一個(gè)控件,使用removeWidget,或者調(diào)用QWidget.hide()一樣可以從布局中刪除,直到QWidget.show()被調(diào)用。下面的垂直布局、網(wǎng)格布局,甚至其他布局都是注意的。
垂直布局
垂直布局(QVBoxLayout)顧名思義,將空間垂直切成多段,然后通過(guò)addWidget、addItem將widget填充指定的位置。如下代碼即實(shí)現(xiàn)了上圖中,細(xì)節(jié)信息的垂直布局(垂直布局中,還嵌套了水平布局):
vbox = QVBoxLayout()
groupBox.setLayout(vbox)
count = QWidget()
hbox = QHBoxLayout()
countLabel = QLabel("細(xì)節(jié)數(shù)目:")
hbox.addWidget(countLabel)
self.countSpineBox = QSpinBox()
self.countSpineBox.setRange(0, 10)
self.countSpineBox.valueChanged.connect(self.countSpineValueChanged)
hbox.addWidget(self.countSpineBox)
hbox.addStretch()
count.setLayout(hbox)
vbox.addWidget(count) #垂直布局,添加widget1
self.detailTable = QTableWidget()
self.detailTable.setColumnCount(9)
self.detailTable.setHorizontalHeaderLabels(
['有效期', '貨幣類型', '價(jià)格', '普通折扣價(jià)', '藍(lán)鉆價(jià)', '藍(lán)鉆折扣價(jià)', '超級(jí)藍(lán)鉆折扣價(jià)', '贈(zèng)送禮包ID', '快捷購(gòu)買'])
vbox.addWidget(self.detailTable) #垂直布局,添加widget2
垂直布局中,還嵌套了水平布局。
說(shuō)明:QHBoxLayout、QVBoxLayout都是繼承自QBoxLayout,為了更好的控制布局,都繼承了以下方法:
l QBoxLayout.addSpacing (size)
添加一個(gè)不能伸縮的空間(一個(gè)QSpacerItem),其寬度設(shè)置為size到布局末尾。框布局提供了默認(rèn)的邊距margin和spacing,這是額外添加的空間。
l QBoxLayout.addStretch(stretch)
添加一個(gè)可伸縮的空間(一個(gè)QSpacerItem),設(shè)0為最小值并且伸縮因子為stretch直到布局末尾
網(wǎng)絡(luò)布局
網(wǎng)格布局(QGridLayout)顧名思義,將空間劃分成多行多列的網(wǎng)絡(luò),然后通過(guò)addWidget、addItem將widget填充到指定的單元格(cell)。這個(gè)比較像網(wǎng)頁(yè)中使用table布局的思路。下面的代碼即創(chuàng)建上圖中的網(wǎng)格布局:
grid = QGridLayout()
grid.addWidget(setidLabel, 0, 0)
grid.addWidget(self.setidLineEdit, 0, 1)
grid.addWidget(QLabel("(第1位-2,第2~3位-表示適用角色,第4~5位-掛點(diǎn)位置,第6~8位-序號(hào))"), 0, 2)
grid.addWidget(subidLabel, 1, 0)
grid.addWidget(self.subidLineEdit, 1, 1)
grid.addWidget(QLabel("(套裝包含的物品,多個(gè)物品適用逗號(hào)分隔;必須在套裝之前添加)"), 1, 2)
grid.addWidget(fashionLabel, 2, 0)
grid.addWidget(self.fashionLineEdit, 2, 1)
grid.addWidget(nameLabel, 3, 0)
grid.addWidget(self.nameLineEdit, 3, 1)
grid.addWidget(descLabel, 4, 0)
grid.addWidget(self.descLineEdit, 4, 1)
grid.addWidget(marketTagLabel, 5, 0)
grid.addWidget(self.tagCombox, 5, 1)
grid.addWidget(recommendLabel, 6, 0)
grid.addWidget(self.recommendCombox, 6, 1)
grid.addWidget(roleLabel, 8, 0)
grid.addWidget(self.roleChkBoxGroup, 8, 1)
grid.addWidget(beginLabel, 9, 0)
grid.addWidget(self.beginTime, 9, 1)
grid.addWidget(endLabel, 10, 0)
grid.addWidget(self.endTime, 10, 1)
gridWidget = QWidget()
gridWidget.setLayout(grid)
上述往網(wǎng)格中添加的widget都是占一個(gè)單元格的情況,其實(shí)還支持占用幾個(gè)單元格。如下代碼,往網(wǎng)格中的第二行、第一列添加一個(gè)widget,占用1行、2列:
grid.addWidget(self.createDetail(), 1, 0, 1, 2)
網(wǎng)格布局默認(rèn)是均分每列,為了更好的控制布局,QGridLayout為每列提供了最小寬度(setColumnMinimumWidth())、伸縮因子(setColumnStretch()),為每行提供了最小高度(setRowMinimumHeight())、伸縮因子(setRowStretch())。最小寬/高度很好理解,伸縮因子如下面代碼,設(shè)置了第二列和三列的比例是1:2。
layout.setColumnStretch(1, 10)
layout.setColumnStretch(2, 20)
3. 二級(jí)彈窗
QDialog類是對(duì)話框窗口的基類。對(duì)話框窗口是主要用于短期任務(wù)以及和用戶進(jìn)行簡(jiǎn)要通訊的頂級(jí)窗口。QDialog可以是模態(tài)對(duì)話框也可以是非模態(tài)對(duì)話框。QDialog支持?jǐn)U展性并且可以提供返回值。它們可以有默認(rèn)按鈕。
內(nèi)置對(duì)話框
內(nèi)置常用的對(duì)話框有:QColorDialog、QErrorMessage、QFileDialog、QFontDialog、QInputDialog、QMessageBox、QProgressDialog、QTabDialog、QWizard。
內(nèi)置的對(duì)話框提供了一些常用的功能,使用起來(lái)也必將遍歷。編寫(xiě)該工具使用到了,選擇文件、目錄的對(duì)話框QFileDialog。
自定義對(duì)話框
如果內(nèi)置的對(duì)話框不能滿足需求,可以自定義對(duì)話框(繼承自QDialog)。如下定義了一個(gè)設(shè)置路徑的對(duì)話框:
class SettingDialog(QDialog):
def __init__(self, parent=None):
super(SettingDialog, self).__init__(parent)
self.path = Global.path
self.initUI()
self.setWindowIcon(QIcon("res/ico/settingPath.ico"))
self.setWindowTitle("設(shè)置")
self.resize(240, 100)
def initUI(self):
grid = QGridLayout()
grid.addWidget(QLabel("路徑:"), 0, 0)
self.pathLineEdit = QLineEdit()
self.pathLineEdit.setFixedWidth(200)
self.pathLineEdit.setText(Global.path)
grid.addWidget(self.pathLineEdit, 0, 1)
button = QPushButton("更改")
button.clicked.connect(self.changePath)
grid.addWidget(button, 0, 2)
grid.addWidget(QLabel("<font color='#ff0000'>包含Keywords.xml、Avatar,AvatarSet,Market.xls的路徑</font>"), 1, 0, 1, 3)
buttonBox = QDialogButtonBox()
buttonBox.setOrientation(Qt.Horizontal) # 設(shè)置為水平方向
buttonBox.setStandardButtons(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
buttonBox.accepted.connect(self.accept) # 確定
buttonBox.rejected.connect(self.reject) # 取消
grid.addWidget(buttonBox, 2, 1)
self.setLayout(grid)
def changePath(self):
open = QFileDialog()
self.path = open.getExistingDirectory()
self.pathLineEdit.setText(self.path)
print(self.path)
使用對(duì)話框,只需要:
dialog = SettingDialog()
if dialog.exec_():
# -----
4. 常用組件
下面介紹編寫(xiě)工具過(guò)程中使用到的組件的一些注意事項(xiàng)。
QTableWidget
列自適應(yīng)
如果有很多列,QTableWidget出出現(xiàn)水平滾動(dòng)條,但是有不希望有滾動(dòng)條可以通過(guò)設(shè)置列自適應(yīng)方式:
tw.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
保證所以列都能顯示,不會(huì)出現(xiàn)水平滾動(dòng)條,這樣有的單元格顯示會(huì)被截?cái)囡@示,如圖中的"青年套裝下裝"-->"青年套裝...",這時(shí)可以設(shè)置單元的tooltip提供完整顯示的途徑。
禁止編輯
編寫(xiě)工具時(shí),有要求QTableWidget展示出來(lái)的數(shù)據(jù)不能編輯,是通過(guò)以下方式實(shí)現(xiàn):
tw.setEditTriggers(QAbstractItemView.NoEditTriggers)
QAbstractItemView還定義了其它的模式,如下表所示:
|
Constant |
Value |
Description |
|
QAbstractItemView.NoEditTriggers |
0 |
No editing possible. |
|
QAbstractItemView.CurrentChanged |
1 |
Editing start whenever current item changes. |
|
QAbstractItemView.DoubleClicked |
2 |
Editing starts when an item is double clicked. |
|
QAbstractItemView.SelectedClicked |
4 |
Editing starts when clicking on an already selected item. |
|
QAbstractItemView.EditKeyPressed |
8 |
Editing starts when the platform edit key has been pressed over an item. |
|
QAbstractItemView.AnyKeyPressed |
16 |
Editing starts when any key is pressed over an item. |
|
QAbstractItemView.AllEditTriggers |
31 |
Editing starts for all above actions. |
按行選擇
設(shè)置QTableWidget按行選擇:
tw.setSelectionBehavior(QAbstractItemView::SelectRows); //整行選中的方式
QAbstractItemView還定義了其它的模式,如下表所示:
|
Constant |
Value |
Description |
|
QAbstractItemView.SelectItems |
0 |
Selecting single items. |
|
QAbstractItemView.SelectRows |
1 |
Selecting only rows. |
|
QAbstractItemView.SelectColumns |
2 |
Selecting only columns. |
表頭排序
如果希望單擊QTableWidget表頭進(jìn)行數(shù)據(jù)排序,可以簡(jiǎn)單通過(guò)以下接口實(shí)現(xiàn):
tw.setSortingEnabled(True)
但是,排序需要注意的2個(gè)問(wèn)題:
l 點(diǎn)了下qtablewidget 的標(biāo)題,它排序正常,修改數(shù)據(jù),在查詢,數(shù)據(jù)顯示有問(wèn)題
重新獲取數(shù)據(jù)之前先關(guān)閉可排序性,獲取到數(shù)據(jù)之后再開(kāi)啟排序性
l 排序規(guī)則問(wèn)題,默認(rèn)使用字母排序
使用以下方式設(shè)置單元格,會(huì)使用字母排序
item = QTableWidgetItem()
item.setData(Qt.DisplayRole, "xxx")
或者
item = QTableWidgetItem()
item.setText("xxx")
如果需要按照數(shù)值排序需要使用以下方式設(shè)置單元格
item = QTableWidgetItem()
item.setData(Qt.DisplayRole, int(1212))
自定義單元格控件
可以對(duì)QTableWidget自定義(添加)widget,如下為QTableWidget設(shè)置單元格為一個(gè)下拉選擇的QCombox
combox = QComboBox()
for _, v in ParseKeyword.currencyType.items():
combox.addItem(v["cname"], v["value"])
combox.setCurrentText("點(diǎn)券")
tw.setCellWidget(row, 1, combox)
效果如下圖所示:
QDateTimeEdit
顯示格式
默認(rèn)的時(shí)間顯示格式(如2015/1/16 17:42),可能不滿足需求,可以通過(guò)setDisplayFormat()設(shè)置顯示格式來(lái)定制。格式選項(xiàng)如下所示:
這些是可能用到的日期表達(dá)式:
l d - 沒(méi)有前置0的數(shù)字的天(1-31)
l dd - 前置0的數(shù)字的天(01-31)
l ddd - 縮寫(xiě)的日名稱(Mon-Sun)。使用QDate.shortDayName()。
l dddd - 長(zhǎng)的日名稱(Monday-Sunday)。使用QDate.longDayName()。
l M - 沒(méi)有前置0的數(shù)字的月(1-12)
l MM - 前置0的數(shù)字的月(01-12)
l MMM - 縮寫(xiě)的月名稱(Jan-Dec)。使用QDate.shortMonthName()。
l MMMM - 長(zhǎng)的月名稱(January-December)。使用QDate.longMonthName()。
l yy - 兩位數(shù)字的年(00-99)
l yyyy - 四位數(shù)字的年(0000-9999)
這些是可能用到的時(shí)間表達(dá)式:
l h - 沒(méi)有前置0的數(shù)字的小時(shí)(0-23或者如果顯示AM/PM時(shí),1-12)
l hh - 前置0的數(shù)字的小時(shí)(00-23或者如果顯示AM/PM時(shí),01-12)
l m - 沒(méi)有前置0的數(shù)字的分鐘(0-59)
l mm - 前置0的數(shù)字的分鐘(00-59)
l s - 沒(méi)有前置0的數(shù)字的秒(0-59)
l ss - 前置0的數(shù)字的秒(00-59)
l z - 沒(méi)有前置0的數(shù)字的毫秒(0-999)
l zzz - 前置0的數(shù)字的毫秒(000-999)
l AP - 切換為AM/PM顯示。AP將被“AM”或“PM”替換。
l ap - 切換為am/pm顯示。ap將被“am”或“pm”替換。
如工具中使用的格式為:
setDisplayFormat("yyyy-MM-dd hh:mm:ss")
顯示效果如下圖所示:
彈出日期選擇窗口
希望點(diǎn)擊QDateTimeEdit可以彈出日期選擇窗口,可以簡(jiǎn)單的通過(guò)setCalendarPopup(True)實(shí)現(xiàn),非常的簡(jiǎn)單。
5. 打包
python常用的打包工具有py2exe、pyinstaller、cx_freeze,而且現(xiàn)在都開(kāi)始支持python3,py2exe可以打包成單exe文件,一般簡(jiǎn)單的東西都是用它來(lái)打包供其他人使用。但是使用py2exe打包PyQt5時(shí),碰到了不少錯(cuò)誤,后面干脆使用cx_freeze打包一次成功(不足之處,就是不能打包成單個(gè)exe)。下面簡(jiǎn)單介紹編寫(xiě)setup.py幾個(gè)關(guān)鍵的點(diǎn),詳細(xì)的參考官方文檔(http://cx-freeze.readthedocs.org/en/latest/index.html)。
l 默認(rèn)只會(huì)打包代碼文件,如果程序有非代碼文件,如配置、資源文件需要打包,需要顯示指定。如"include_files": ["setting.ini", "res"],打包時(shí)會(huì)將setting.ini文件、res資源目錄拷貝到exe目錄下。
l cx_freeze會(huì)自動(dòng)檢測(cè)依賴文件,但是有時(shí)候會(huì)抽風(fēng),可以通過(guò)"packages": ["os", "xlrd3", "xlwt3", "lxml"]顯示包含。同時(shí)對(duì)不要的包,可以"excludes": ["tkinter"]指定不要編譯到最終的軟件包中。
l 指定文件名需要帶exe后綴,cx_freeze是不會(huì)自動(dòng)添加exe后綴的。
l 如果需要一次編譯多個(gè)exe,可以在executables數(shù)組中列出多個(gè),例如:
executables = [
Executable("main.py", base=base, targetName="Joker3DAvatarMgr.exe", compress=True, icon="res/ico/icon.ico"),
Executable("test.py", base=base, targetName="test.exe", compress=True, icon="res/ico/test.ico")
]
完整的setup.py文件如下所示:
import sys
from cx_Freeze import setup, Executable
# GUI applications require a different base on Windows (the default is for a
# console application).
base = None
if sys.platform == "win32":
base = "Win32GUI"
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {
"packages": ["os", "xlrd3", "xlwt3", "lxml"],
"excludes": ["tkinter"],
"include_files": ["setting.ini", "res"]
}
#
executables = [
Executable("main.py", base=base, targetName="Joker3DAvatarMgr.exe", compress=True, icon="res/ico/icon.ico")
]
setup( name = "setup",
version = "0.1",
description = "Joker3D prop manager tool!",
author = "tylerzhu",
author_email = "saylor.zhu@gmail.com",
options = {"build_exe": build_exe_options},
executables = executables,
)
編寫(xiě)好setup.py之后,可以通過(guò)python setup.py build打包。
網(wǎng)上有不少人反饋打包之后,放到?jīng)]有按照PyQt的PC上執(zhí)行,會(huì)報(bào)以下錯(cuò)誤:“This application failed to start because it could not find or load the Qt platform plugin windows”
這個(gè)問(wèn)題,我以前也碰到過(guò),但是這次我用的Python3.4 + cx_freeze 4.3.4 + PyQt5-5.4-gpl-Py3.4-Qt5.4.0-x32.exe并沒(méi)有出現(xiàn)這個(gè)問(wèn)題。如果出現(xiàn)了這個(gè)問(wèn)題也不要緊,通過(guò)以下方法可以解決:將PyQt5安裝目錄(Lib\site-packages\PyQt5)下的libGLESv2.dll拷到打包的exe目錄下即可。

浙公網(wǎng)安備 33010602011771號(hào)