Портирование 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))