0

我在将“使用 Python 和 Qt 的快速 GUI 编程”中的程序示例从 PyQt4 移植到 PyQt5 时遇到了麻烦。示例程序演示了一个 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 确实有一个文件名参数。而 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 类型的。我认为这将是一个 str 类型。

4

1 回答 1

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))
于 2017-04-04T14:30:41.123 回答