Sprachumschaltung ohne Neustart

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

bisher verfahre ich so, dass bei einer Sprachumschaltung mein Programm dazu angehalten wird neuzustarten. Wenn der Anwender die Sprache im Programm ändert (Beispiel: von englisch auf deutsch), dann startet sich mein Programm neu, und lädt die entsprechende Datei, die von Qt-Linguist bearbeitet wurde. Nun möchte ich dazu übergehen, dass man sich den Neustart erspart. Dazu habe ich mir soeben ein kleines Programm gebastet -leider nicht ausführbar. Das Programm funktioniert auch wunderbar, allerdings habe ich kleine Bedenken. Seht selbst:

Code: Alles auswählen

import sys
from PyQt4 import QtGui, QtCore
from testlangs import Ui_MainWindow

class Test(QtGui.QMainWindow, Ui_MainWindow):
	def __init__(self, app):
		QtGui.QMainWindow.__init__(self)
		Ui_MainWindow.__init__(self)
		# save reference to app for later
		self._app = app
		self.set_translators()
		self._app.installTranslator(self.en_translator)
		# Configure ui interface
		self.setupUi(self)
		# Connect menu items
		self.actionDeutsch.triggered.connect(lambda: self.change_language("langs_de",
                                                                               self.en_translator,
                                                                               self.de_translator))
		
		self.actionEnglish.triggered.connect(lambda: self.change_language("langs_en",
                                                                               self.de_translator,
                                                                               self.en_translator))

	# Create the translators for English and German
	def set_translators(self):                
		self.en_translator = QtCore.QTranslator(self._app)
		self.en_translator.load("langs_en")
		self.de_translator = QtCore.QTranslator(self._app)
		self.de_translator.load("langs_de")

	# Change the language to German
	def change_language(self, arg, del_trans, add_trans):
		self._app.removeTranslator(del_trans)
		self._app.installTranslator(add_trans)
		self.retranslateUi(self)
		
if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	window = Test(app)
	window.show()
	sys.exit(app.exec_())
Was mich besonders nervt, ist die Wartungsarbeit der set_translators()-Funktion. Sämtliche Sprachen müssen dort "verwaltet" werden. Und in der change_language()-Funktion werden neue Sprachen installiert, und die Sprache zuvor wird gelöscht. Hier müssen also zwei Funktionen regelmäßig gewartet werden, sobald eine neue Sprache hinzukommt oder eine Sprache weg fällt. Des Weiteren missfällt mir meine Lösung mit dem Menu. Sobald im Menu die Sprache "deutsch" gewählt wird, wird die Sprache "englisch" entfernt. Andersrum genauso: wird die Sprache "englisch" gewählt, wird die Sprache "deutsch" gelöscht. Und jetzt stellen wir uns mal vor, das Programm würde an die 5-10 Sprachen "beherrschen"? Ich empfinde meine Lösung als grauenhaft. Ich stecke also gedanklich fest.

Daher wollte ich euch fragen, ob jemand von euch eine bessere Lösung parat hat und wie ihr vorgehen würdet?

Gruß
Sophus
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Verändere den Code so dass ein Dictionary {str: QTranslator} darin vorkommt und sinnvoll verwendet wird. Tipp: Die action{Deutsch,English} und {de,en}_translator Attribute sollten danach nicht mehr existieren.

Code zu generieren ist übrigens nicht nur unnötig sondern grundsätzlich ziemlich schwierig sauber hinzubekommen. Lass es sein und beschäftige dich damit wie du UI Dateien zur Laufzeit lädst.
Sirius3
User
Beiträge: 17752
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: es macht ja wenig Sinn, immer alle Sprachen am Anfang zu laden. Normalerweise hat man ein Verzeichnis mit Übersetzungsdateien, das man durchgeht, wenn man die Sprache ändern will und nur die aktuelle Sprache, die man in der Klasse speichern muß.

Code: Alles auswählen

class Test(QtGui.QMainWindow):
    def __init__(self, app):
        QtGui.QMainWindow.__init__(self)
        self._app = app
        self.current_translator = QtCore.QTranslator(self._app)
        self.current_translator.load("langs_en")
        self._app.installTranslator(self.current_translator)
        # Configure ui interface
        loadUi('mainwindow.ui', self)
        # Connect menu items
        # sollte man eigentlich dynamisch erzeugen:
        self.actionDeutsch.triggered.connect(lambda: self.change_language("langs_de"))
        self.actionEnglish.triggered.connect(lambda: self.change_language("langs_en"))

    # Change the language to German
    def change_language(self, language):
        self._app.removeTranslator(self.current_translator)
        self.current_translator = QtCore.QTranslator(self._app)
        self.current_translator.load(language)
        self.retranslateUi(self)
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo!

@Sirui3: Hast du in deinem Beispiel-Quelltext in deiner change_language()-Funktion die installTranslator()-Methode vergessen oder sie bewusst weggelassen? Denn ich ging immer davon aus, dass, nachdem die load()-Methode durchlaufen ist, die installTranslator()-Methode aufgerufen werden muss.
Hinweis: In meinem Mini-Projekt werden die Sprachdateien als Kompilat in einer Resource-Datei gespeichert, und nicht so "lose" in Ordnern. Des Weiteren werden die Sprache in eine ComboBox strukturiert. Das mit dem Menu diente eigentlich für die schnelle Bearbeitung des Designer-Problems.

@DasIch: In der Regel lade ich die *.ui-Dateien zur Laufzeit dynamisch. Hier wollte ich lediglich ein Beispiel anführen.

Ich habe jedoch einige Ratschläge von euch zur Kenntnis genommen und sie entsprechend umgesetzt. Der übersichtshalber habe ich mir die Freiheit genommen und zwei Funktionen definiert: einmal init_current_language() und einmal init_connect_menu_item(). Diese werden beim Start des Programms auch aufgerufen.

Code: Alles auswählen

import sys
from PyQt4.uic import loadUi
from PyQt4.QtCore import Qt, QFile, QMetaObject
from PyQt4 import QtCore
from PyQt4.QtGui import QMainWindow, QApplication

class Test_Window(QMainWindow):
    def __init__(self,
                 app):
        QMainWindow.__init__(self, parent=None)

        UI_PATH = QFile("testlangs.ui")

	# save reference to app for later
        self._app = app

        # Load ui dynamically
        UI_PATH.open(QFile.ReadOnly)
        self.ui = loadUi(UI_PATH, self)
        UI_PATH.close()

        self.init_current_language()
        self.init_connect_menu_item()


    def init_current_language(self):
        self.current_translator = QtCore.QTranslator(self._app)
        self.current_translator.load("langs_en")
        self._app.installTranslator(self.current_translator)


    def init_connect_menu_item(self):
        self.actionDeutsch.triggered.connect(lambda: self.change_language("langs_de"))
        self.actionEnglish.triggered.connect(lambda: self.change_language("langs_en"))

 
    # Change the language to German
    def change_language(self, language):
        self._app.removeTranslator(self.current_translator)
        self.current_translator = QtCore.QTranslator(self._app)
        self.current_translator.load(language)
        self._app.installTranslator(self.current_translator)
        self.retranslateUi(self.ui)
Allerdings bekomme ich ein kleines Problem. Beim Ausführen des Programms tauchen zunächst keine Fehler auf. Wenn ich aber dazu übergehe, die Sprache zu ändern, bekomme ich folgenden Tracback:
self.retranslateUi(self)
AttributeError: 'Test_Window' object has no attribute 'retranslateUi'
Ich kann mir die Meldung nur damit erklären, weil hie die *.ui-Datei dynamisch geladen wird. In meinem ersten Beispiel wurde die generierte Benutzeroberfläche geladen. Denn die in Python generierte Oberfläche hatte ja die retranslateUi()-Methode inne, die hier gänzlich fehlt. Muss ich jetzt händisch und zu Fuß alle Widgets angeben, die erneut übersetzt werden soll?

Etwa so? Meine retranslateUi()-Methode sähe dann wie folgt aus:

Code: Alles auswählen

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(self._app.translate("MainWindow", "MainWindow", None, self._app.UnicodeUTF8))
        self.nameLabel.setText(self._app.translate("MainWindow", "Name", None, self._app.UnicodeUTF8))
        self.ageLabel.setText(self._app.translate("MainWindow", "Age", None, self._app.UnicodeUTF8))
        self.menuMenu.setTitle(self._app.translate("MainWindow", "File", None, self._app.UnicodeUTF8))
        self.menuEdit.setTitle(self._app.translate("MainWindow", "Edit", None, self._app.UnicodeUTF8))
        self.menuLanguage.setTitle(self._app.translate("MainWindow", "Language", None, self._app.UnicodeUTF8))
        self.actionOpen.setText(self._app.translate("MainWindow", "Open", None, self._app.UnicodeUTF8))
        self.actionSave.setText(self._app.translate("MainWindow", "Save", None, self._app.UnicodeUTF8))
        self.actionClear.setText(self._app.translate("MainWindow", "Clear", None, self._app.UnicodeUTF8))
        self.actionEnglish.setText(self._app.translate("MainWindow", "English", None, self._app.UnicodeUTF8))
        self.actionDeutsch.setText(self._app.translate("MainWindow", "Deutsch", None, self._app.UnicodeUTF8))
Ich weiß allerdings auch, dass es die findChild ()-Methode in der QObject()-Klasse gibt. Allerdings erscheint mir das sehr mühselig, anzugeben, dass alle ComboBox, PushButton, LineEdit etc. aufgespürt werden sollen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ich wurde durch das Thema, was mich beschäftigt, auf eine weitere Methode aufmerksam, wie man eine *.ui-Datei dynamisch zur Laufzeit laden kann. Ich rede von der loadUiType ()-Methode. Nun frage ich mich, was der Unterschied zwischen loadUi() und loadUiType() ist. arbeitet einer langsamer als der andere? Was ich bei der loadUiType()-Methode beobachtet habe, ist, dass sie außerhalb einer Klasse erstellt wird, während die loadUi()-Methode innerhalb einer View-Klasse erstellt wird. Aber gibt es weitere signifikante Unterschiede?
Antworten