記得有一次打開一個單獨exe程序,點擊btn中的一個幫助說明按鈕,在同級目錄下就多出一個help.chm 文件并自動打開。
那這個exe肯定是把help.chm 打包到exe中,當我觸發“幫助”按鈕的時候另存為help.chm 并打開該文件。
所以我在想,Pyqt打包資源是否也可以另存為后打開資源中的文件呢?然后就有了下文
我們先找幾個資源文件
比如:

用Qt Designer中的資源瀏覽器把資源素材添加并保存為resexe.qrc 文件

resexe.qrc文件:
<RCC> <qresource PRefix="exe"> <file>aaa/ResHacker3.5.exe</file> </qresource> <qresource prefix="chm"> <file>aaa/PyQt4_Tutorial.chm</file> </qresource> <qresource prefix="txt"> <file>aaa/texta.txt</file> </qresource> <qresource prefix="mp3"> <file>aaa/apples.mp3</file> </qresource></RCC>
將qrc資源文件轉換為py
pyrcc4 -o resexe.py resexe.qrc
用Python讀取保存二進制文件是這樣的:
#讀取資源文件 originalfile = open('F:/QQ.exe','rb')filedata = originalfile.read()originalfile.close()#指定為字節類型savedata = bytearray(filedata)savefile = open('C:/QQ_res.exe','wb')savefile.write(savedata)savefile.close()
但在Pyqt中py原生的open() 方法是找不到文件的:
因為Qt qrc轉換的資源必須要用":"開始引用,例如:
self.setWindowIcon(QIcon(':qq.ico'))
以下代碼是錯誤的:
originalfile = open(':mp3/aaa/apples.mp3','rb')filedata = originalfile.read()originalfile.close()savedata = bytearray(filedata)savefile = open('C:/apples.mp3','wb')savefile.write(savedata)savefile.close()
報錯:
Traceback (most recent call last): originalfile = open(':mp3/aaa/apples.mp3','rb') IOError: [Errno 22] invalid mode ('rb') or filename: ':mp3/aaa/apples.mp3'
所以直接使用Py的open()方法是無法獲取Qt qrc資源文件的。
要獲取qrc里面的資源文件必須要使用Qt內置的QFile
下面有兩個方法
QtCore.QFile.copy(':mp3/aaa/apples.mp3','C:/appless.mp3')
可直接將資源文件copy到指定的目錄
QFile.copy文檔:
| bool QFile.copy (self, QString newName)Copies the file currently specified by fileName() to a file called newName. Returns true if successful; otherwise returns false. Note that if a file with the name newName already exists, copy() returns false (i.e. QFile will not overwrite it). The source file is closed before it is copied. See also setFileName(). bool QFile.copy (QString fileName, QString newName)This is an overloaded function. Copies the file fileName to newName. Returns true if successful; otherwise returns false. If a file with the name newName already exists, copy() returns false (i.e., QFile will not overwrite it). See also rename(). | 
originfiles = QtCore.QFile(':mp3/aaa/apples.mp3')originfiles.open(QtCore.QFile.ReadOnly)origindata = originfiles.readAll()savefiledata = bytearray(origindata)savefile = open('C:/appless.mp3', 'wb')savefile.write(savefiledata)savefile.close()
QFile以只讀模式打開資源':mp3/aaa/appless.mp3', readAll() 返回二進制QByteArray, 再通過Py的open() 以'wb'模式寫入二進制數據
QIODevice.readAll文檔:
| object QIODevice.read (self, long maxlen)Reads at most maxSize bytes from the device into data, and returns the number of bytes read. If an error occurs, such as when attempting to read from a device opened in WriteOnly mode, this function returns -1. 0 is returned when no more data is available for reading. However, reading past the end of the stream is considered an error, so this function returns -1 in those cases (that is, reading on a closed socket or after a process has died). See also readData(), readLine(), and write(). QByteArray QIODevice.readAll (self)This is an overloaded function. Reads all available data from the device, and returns it as a QByteArray. This function has no way of reporting errors; returning an empty QByteArray() can mean either that no data was currently available for reading, or that an error occurred. | 
完整代碼如下:
# -*- coding: utf-8 -*-'''下載打包資源文件中的資源'''from PyQt4 import QtCore, QtGuiimport sys, osreload(sys)sys.setdefaultencoding("utf-8")class Mwindow(QtGui.QDialog): def __init__(self): super(Mwindow, self).__init__() self.resize(100, 150) self.setWindowTitle(u'下載打包文件中的資源文件') self.down1 = QtGui.QPushButton(u'下載ResHacker') self.down2 = QtGui.QPushButton(u'下載PyQt4_Tutorial') self.down3 = QtGui.QPushButton(u'下載texta文本') self.down4 = QtGui.QPushButton(u'下載apples.mp3') self.checked = QtGui.QCheckBox(u'同時打開文件') self.checked.setCheckState(QtCore.Qt.Checked) mylayout = QtGui.QGridLayout() mylayout.addWidget(self.down1, 0, 0) mylayout.addWidget(self.down2, 0, 2) mylayout.addWidget(self.down3, 0, 1) mylayout.addWidget(self.down4, 1, 0) mylayout.addWidget(self.checked, 1, 2) self.setLayout(mylayout) self.connect(self.down1, QtCore.SIGNAL('clicked()'), self.download) self.connect(self.down2, QtCore.SIGNAL('clicked()'), self.download) self.connect(self.down3, QtCore.SIGNAL('clicked()'), self.download) self.connect(self.down4, QtCore.SIGNAL('clicked()'), self.download) def download(self): import resexe senderc = str(self.sender().text()) downObject = '' extend = 'All Files (*.*)' if senderc.find('appl') > 0: downObject = ':mp3/aaa/apples.mp3' extend = 'mp3 Files (*.mp3)' if senderc.find('ResHacker') > 0: downObject = ':exe/aaa/ResHacker3.5.exe' extend = 'exe Files (*.exe)' if senderc.find('PyQt4_Tutorial') > 0: downObject = ':chm/aaa/PyQt4_Tutorial.chm' extend = 'chm Files (*.chm)' if senderc.find('text') > 0: downObject = ':txt/aaa/texta.txt' extend = ' Files (*.txt)' fileName = QtGui.QFileDialog.getSaveFileName(self, u"文件保存", "C:/", extend) if fileName: # 方法一 # QtCore.QFile.copy(downObject,fileName) #方法二 originfiles = QtCore.QFile(downObject) originfiles.open(QtCore.QFile.ReadOnly) origindata = originfiles.readAll() savefiledata = bytearray(origindata) savefile = open(fileName, 'wb') savefile.write(savefiledata) savefile.close() openfile = self.checked.isChecked() #判斷選擇打開文件 if openfile: os.system(str(fileName)) else: QtGui.QMessageBox.question(self, (u'提示'), (u'保存成功'), QtGui.QMessageBox.Yes)if __name__ == '__main__': app = QtGui.Qapplication(sys.argv) mainWin = Mwindow() mainWin.show() sys.exit(app.exec_())
我們使用Pyinstaller打包
if __name__ == '__main__': from PyInstaller.main import run params=['downloadres.py', '-F', '-w', '--icon=favicon.ico'] run(params)
生成downloadres.exe






如果可以,接下來我們也可以做一個tcp獲取網絡資源并下載的Pyqt程序。
新聞熱點
疑難解答