将PyQt4 QWorkspace移植到PyQt5 QMdiArea - subWindowList方法

时间:2016-06-13 22:18:29

标签: pyqt pyqt5 qtextedit qmdiarea

我遇到了从PyQt4到PyQt5的“使用Python和Qt进行快速GUI编程”的程序示例。示例程序演示了一个MDI应用程序,可以在主窗口中运行多个文本编辑窗口。

我使用python 3.4.4和PyQt 4.8.7作为PyQt4版本。我使用python 3.4.4和PyQt 5.5.1作为PyQt5版本。

我首先将所有旧式信号定义更改为原始PyQt4程序中的新样式信号。在PyQt 4.5中实现了新的样式信号,因此我可以使用这些更改运行原始程序。在将所有旧式信号更新为新式信号后,应用程序成功运行。

原始程序使用PyQt4.QtGui.QWidget.QWorkspace类来实现MDI工作区。 QWorkspace被PyQt4.3中的PyQt5.QtWidgets.QMdiArea类所取代。我的问题浮出水面,试图修改原始代码以使用QMdiArea。

使用自定义TextEdit小部件的实例(QTextEdit的子类)来呈现和编辑每个文本文档。

MDI应用程序的最小PyQt5版本 - texteditor.py

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *

class TextEdit(QTextEdit):
    NextId = 1

    def __init__(self, filename="", parent=None):
        print("TextEdit __init__")
        super(TextEdit, self).__init__(parent)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.filename = filename
        if not self.filename:
            self.filename = "Unnamed-{}.txt".format(TextEdit.NextId)
            TextEdit.NextId += 1
        self.document().setModified(False)
        self.setWindowTitle(QFileInfo(self.filename).fileName())

    def load(self):
        print("load - TextEdit")
        exception = None
        fh = None
        try:
            fh = QFile(self.filename)
            if not fh.open(QIODevice.ReadOnly):
                raise IOError(fh.errorString())
            stream = QTextStream(fh)
            stream.setCodec("UTF-8")
            self.setPlainText(stream.readAll())
            self.document().setModified(False)
        except EnvironmentError as e:
            exception = e
        finally:
            if fh is not None:
                fh.close()
            if exception is not None:
                raise exception

import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *

__version__ = "1.0.0"

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.mdi = QMdiArea()
        self.setCentralWidget(self.mdi)
        fileOpenAction = QAction("&Open...", self)
        fileOpenAction.setShortcut(QKeySequence.Open)
        fileOpenAction.triggered.connect(self.fileOpen)
        fileMenu = self.menuBar().addMenu("&File")
        fileMenu.addAction(fileOpenAction)
        settings = QSettings()
        self.restoreGeometry(settings.value("MainWindow/Geometry",
                QByteArray()))
        self.restoreState(settings.value("MainWindow/State",
                QByteArray()))
        QTimer.singleShot(0, self.loadFiles)


    def loadFiles(self):
        if len(sys.argv) > 1:
            for filename in sys.argv[1:31]: # Load at most 30 files
                if QFileInfo(filename).isFile():
                    self.loadFile(filename)
                    QApplication.processEvents()
        else:
            settings = QSettings()
            files = settings.value("CurrentFiles") or []
            for filename in files:
                if QFile.exists(filename):
                    self.loadFile(filename)
                    QApplication.processEvents()  #todo What does this do?

    def fileOpen(self):
        filename, _ = QFileDialog.getOpenFileName(self,
                "Text Editor -- Open File")
        if filename:
            for textEdit in self.mdi.subWindowList():
                print(type(textEdit))
                if textEdit.filename == filename:
                    self.mdi.setActiveSubWindow(textEdit)
                    break
            else:
                self.loadFile(filename)

    def loadFile(self, filename):
        textEdit = TextEdit(filename)
        try:
            textEdit.load()
        except EnvironmentError as e:
            QMessageBox.warning(self, "Text Editor -- Load Error",
                    "Failed to load {}: {}".format(filename, e))
            textEdit.close()
            del textEdit
        else:
            self.mdi.addSubWindow(textEdit)
            textEdit.show()

app = QApplication(sys.argv)
app.setWindowIcon(QIcon(":/icon.png"))
app.setOrganizationName("Qtrac Ltd.")
app.setOrganizationDomain("qtrac.eu")
app.setApplicationName("Text Editor")
form = MainWindow()
form.show()
app.exec_()

fileOpen()方法出现问题:

PyQt4 fileOpen()方法

    def fileOpen(self):
        filename = QFileDialog.getOpenFileName(self,
                "Text Editor -- Open File")
        if filename:
            for textEdit in self.mdi.windowList():
                if textEdit.filename == filename:
                    self.mdi.setActiveWindow(textEdit)
                    break
            else:
                self.loadFile(filename)

PyQt5 fileOpen()方法

    def fileOpen(self):
        filename, _ = QFileDialog.getOpenFileName(self,
                "Text Editor -- Open File")
        if filename:
            for textEdit in self.mdi.subWindowList():
                if textEdit.filename == filename:
                    self.mdi.setActiveSubWindow(textEdit)
                    break
            else:
                self.loadFile(filename)

windowList()在PyQt5中实现为subWindowList()。问题是在PyQt4版本中,当执行for textEdit in self.mdi.windowList():时,textEdit的类型是TextEdit所以下一行

if textEdit.filename == filename

有效,因为TextEdit确实有 filename 参数。和textEdit是一个{TextEdit} textedit.TextEdit对象,但是在PyQt5版本中,执行for textEdit in self.mdi.subWindowList():后,textEdit的类型是QMdiSubWindow,所以回溯当然会生成:

Traceback (most recent call last):
  File "texteditor3.py", line 292, in fileOpen
    if textEdit.filename == filename:
AttributeError: 'QMdiSubWindow' object has no attribute 'filename'

令我感到困惑的是PyQt4版本中的textEdit如何成为TextEdit类型。我认为这将是一种类型。

1 个答案:

答案 0 :(得分:0)

我来自德国,我找到了答案。见代码。 抱歉德国评论。

def fileOpen(self):
    try:
        # PSc QFileDialog.getOpenFileName gibt ein Tuple zurück
        # für die weitere Verwendung filname[0] verwenden
        filename = QFileDialog.getOpenFileName(self,
                "Text Editor -- Open File")
        if filename:
            try:
                # PSc wenn ein zweites Open durchgeführt wird erhält man die Fehlermeldung
                # textEdit has no attribute fileName
                # http://stackoverflow.com/questions/37800036/porting-pyqt4-qworkspace-to-pyqt5-qmdiarea-subwindowlist-method
                # Lösung scheinbar hier gefunden Zeile 268 269
                # http://nullege.com/codes/show/src%40p%40y%40pyqt5-HEAD%40examples%40mainwindows%40mdi%40mdi.py/168/PyQt5.QtWidgets.QMdiArea.subWindowActivated.connect/python
                # Folgende Zeile dementsprechen geändert
                # for textEdit in self.mdi.subWindowList():
                for windows in self.mdi.subWindowList():
                    textEdit = windows.widget()
                    print('In File Open textEdit.filename: ' + textEdit.filename)
                    if textEdit.filename == filename[0]:
                        self.mdi.setActiveWindow(textEdit)
                        break
                else:
                    # PSc filename Tuple daher filename[0] übergeben
                    self.loadFile(filename[0])
            except:
                e = sys.exc_info()
                print('An exception occurred in def fileOpen  if Filename : \n' + str(e) + '\n')
    except:
        e = sys.exc_info()
        print('An exception occurred in def fileOpenin: \n' + str(e) + '\n')

我之所以改变它:

def fileSave(self):
    try:
        # PSc PyQt4 Syntax
        # textEdit = self.mdi.activeSubWindow()
        # geändert laut Zeile 268,269
        # nullege.com/codes/show/src%40p%40y%40pyqt5-HEAD%40examples%40mainwindows%40mdi%40mdi.py/168/PyQt5.QtWidgets.QMdiArea.subWindowActivated.connect/python
        window = self.mdi.activeSubWindow()
        textEdit = window.widget()
        if textEdit is None or not isinstance(textEdit, QTextEdit):
            return True
        try:
            textEdit.save()
            return True
        except EnvironmentError as e:
            QMessageBox.warning(self, "Text Editor -- Save Error",
                    "Failed to save {}: {}".format(textEdit.filename, e))
            return False
    except Exception as error:
        print('An exception occurred in fileSave: {}'.format(error))