Портирование PyQt4 QWorkspace в PyQt5 QMdiArea - метод subWindowList

Я столкнулся с трудностью переноса примера программы из "Быстрого программирования с использованием графического интерфейса на Python и Qt" с 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 был заменен классом PyQt5.QtWidgets.QMdiArea в PyQt4.3. Моя проблема возникла при попытке изменить исходный код для работы с QMdiArea.

Каждый текстовый документ представлен и отредактирован с использованием экземпляра пользовательского виджета TextEdit, подкласса QTextEdit.

Минимальная PyQt5-версия приложения MDI - 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'

Что меня действительно сбивает с толку, так это то, как textEdit в версии PyQt4 становится типом TextEdit. Я думаю, что это будет str тип.

1 ответ

Я из Германии, я нашел ответ. Смотрите код. Извините за немецкие комментарии.

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://stackru.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))
Другие вопросы по тегам