Fenster mit QHBoxLayout schließen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

BlackJack hat geschrieben:@Sophus: Jetzt hast Du ein paar Programme aufgezählt bei denen man die Sprache im Programm umstellen kann. Und nun? Das ändert nichts daran das man das bei der Mehrzahl der Programme nicht machen kann, und ist auch kein Argument dafür das man das tatsächlich braucht und das man kurzsichtig handelt wenn man diese Funktionalität nicht zur Verfügung stellt. Denn dafür müsste es dafür Bedarf bei vielen Benutzern geben. Den ich nicht sehe. Und zwar nicht weil ich mir das nicht vorstellen kann, sondern weil ich den in der Praxis tatsächlich nicht sehe.

VLC kann ich übrigens nicht nachvollziehen, denn da habe ich keine Einstellungsmöglichkeit gefunden. Mein Frontend basiert auf Qt.

Skype und JDownloader sind was das UI angeht ziemliche Fremdkörper, was an sich schon nicht so toll ist. Vielleicht wollten die sich auf diese Art sparen einen systemunabhängigen weg zu finden die Systemsprache heraus zu finden.
Ernsthaft jetzt? Nun zu VLC, gehe in der Menüleiste auf "Extras" (English: Tool), dann auf den letzten Menupunkt "Einstellungen" (Englisch: Preferences). Ein weiteres Fenster öffnet sich, links in der Navigation wählst du "Interface" und gleich auf der rechten Seite oben an der ersten Stelle kannst du die Sprache wählen. Du hast gleich eine ganze Reihe von Sprachen zur Verfügung. Und die Frage ob deine Anmerkung ernsthaft gewesen sei? JDownloader, Skype, VLC und einige Programme mehr sind Programme die man nicht nur lokal kennt, sondern weit hinaus recht bekannt sind. Und sie verfahren dennoch so, dass man Sprachen auswählen kann, und nicht nach dem Betriebssystem gehen - zumindest nicht zwangsläufig, sondern bieten es eher optional an, indem sie bei der Sprachauswahl "Automatisch" anbieten. Ich will jetzt keine Programmliste führen, ich wollte dir nur an den bekanntesten Programmen zeigen, dass sie die Idee mit der Sprachauswahl fortführen und das ich es als Anwender toll finde. Keine Ahnung wie du da auf eine andere Ansicht kommst. Aber ich möchte an dieser Stelle die endlosen Streitigkeiten einstellen, denn mein Ausgangsproblem ist immer noch die Selbe. 5-6 Einträge zuvor habe ich meinen Code veröffentlicht.
BlackJack

@Sophus: Ja ernsthaft, so eine Einstellung habe ich nicht in VLC. Weil ich die dort auch gar nicht brauche. Wenn ich das auf Deutsch haben möchte, dann stelle ich entweder den Desktop auf Deutsch um, oder ich starte nur VLC auf Deutsch, was ich aber ausserhalb des Programms einstelle und deshalb im Programm selbst keine Einstellung dafür benötige. Und das funktioniert bei jedem Programm so das mehrere Sprachen unterstützt. Es macht ja auch Sinn das ausserhalb vom Programm zu lösen, damit nicht jeder Programmierer für jedes Programm diese Sprachauswahl aufs neue lösen muss, und es in jedem Programm irgendwie anders geregelt ist. Vielleicht gibt es diese Einstellung im Windows-Frontend von VLC weil eine Spracheinstellung für einzelne Programme unter Windows nicht auf Systemebene gelöst ist wo es eigentlich hingehört‽
BlackJack

@Sophus: Zum eigentlichen Thema, erster Startversuch:

Code: Alles auswählen

$ python MDIForm.py
  File "MDIForm.py", line 14
SyntaxError: Non-ASCII character '\xc3' in file MDIForm.py on line 14, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
Korrigiert, und zweiter Anlauf. Rumgeklickt. Nix passiert. Konsolenausgabe:

Code: Alles auswählen

$ python MDIForm.py
Dieses Programm läuft als Main.
Traceback (most recent call last):
  File "MDIForm.py", line 27, in CreateNewMovie
    self.MDIChild = start_window(self)
NameError: global name 'start_window' is not defined
Mal geraten, und vermutet dass Du da wohl ein `MyForm`-Objekt erstellen wolltest, denn sonst wird das ja nirgends verwendet und um das geht es ja eigentlich. Also korrigiert und dritter Anlauf. Rumgeklickt. Nix passiert. Konsolenausgabe:

Code: Alles auswählen

$ python MDIForm.py
Dieses Programm läuft als Main.
Traceback (most recent call last):
  File "MDIForm.py", line 27, in CreateNewMovie
    self.MDIChild = MyForm(self)
  File "/home/bj/tmp/forum/qt/TestDialog.py", line 18, in __init__
    self.layout().addLayout(pHBox)
AttributeError: 'NoneType' object has no attribute 'addLayout'
Hast Du das tatsächlich mal am laufen gehabt?

Das Problem dürfte jedenfalls sein, dass Du ein „top level window” (`QDialog`) in einem MDI-Fenster verwendest. Das geht nicht.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ja, ich habe das Programm lauffähig vor meiner Nase.

Hier mein Projekt in einer RAR-Datei. Entpacke sie einfach, lasst die Ordnerstruktur so wie sie sind, und es wird auch funktionieren. Unter Windows einfach die Batch-Datei starten oder die Start-Datei Xarphus.py oder Xarphus.pyw starten. Und die Datei TestDialog.py befindet sich im Ordner Xarphus.

HIer mein Projekt:
Xarphus

Anmerkung: Hier habe ich meine Vorgehensweise bezüglich der Sprachmodule nicht entfernt. Aber dies sollte nun kein Problem und zur Ablenkung beitragen.
BlackJack

@Sophus: Wenn ich das Starte und auf Film hinzufügen gehe, dann passiert nichts, und in der Konsole wird mir das hier ausgegeben:

Code: Alles auswählen

$ python Xarphus.py 
Die Ordner existieren bereits.
Das Programm wird von einem anderen Modul importiert.
Traceback (most recent call last):
  File "/home/bj/tmp/forum/Xarphus/Xarphus/MDIForm.py", line 100, in CreateNewMovie
    self.MDIChild = start_window(self)
NameError: global name 'start_window' is not defined
Also genau das Problem das ich auch beim verkürzten Programm hatte.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

BlackJack hat geschrieben:@Sophus: Wenn ich das Starte und auf Film hinzufügen gehe, dann passiert nichts, und in der Konsole wird mir das hier ausgegeben:

Code: Alles auswählen

$ python Xarphus.py 
Die Ordner existieren bereits.
Das Programm wird von einem anderen Modul importiert.
Traceback (most recent call last):
  File "/home/bj/tmp/forum/Xarphus/Xarphus/MDIForm.py", line 100, in CreateNewMovie
    self.MDIChild = start_window(self)
NameError: global name 'start_window' is not defined
Also genau das Problem das ich auch beim verkürzten Programm hatte.
Einen Moment, ich schaue mal nach.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

In der MDIForm.py Datei folgendes ändern:

Code: Alles auswählen

    def CreateNewMovie(self):                   # Neues Fenster für Film hinzufügen öffnen
        self.MDIChild = MyForm(self)  #<---- ansttt start_window kommt hier MyForm hin.
        self.mdiArea.addSubWindow(self.MDIChild)
        self.MDIChild.show()
        print "Dialog lädt"
EmaNymton
User
Beiträge: 174
Registriert: Sonntag 30. Mai 2010, 14:07

Hab dir mal ein Beispiel gebastelt, damit du siehst, das Internationalisierung kein Hexenwerk ist und schon sehr bequem umgesetzt werden kann:
https://github.com/Cermit/snippets/tree ... 8n_example

Vorgehen:
Du erzeugst deinen Quelltext (hier main.py). Alle Strings, die übersetzt werden sollen, setzt du in self.tr().
Du legst ein pro-File (hier project.pro) an, in dem die Dateien aufgeführt werden, in denen Strings zur Übersetzung vorgesehen sind.
Du benutzt das CLI-Tool

Code: Alles auswählen

pylupdate5 project.pro
bzw

Code: Alles auswählen

pylupdate4 project.pro
(je nach verwendeter PyQt-Version) um die ts-Files zu erzeugen (sind nichts anderes als xml-Files).
Du benutzt den Qt Linguist, um die Übersetzungen anzugeben und gibst zum Schluss dort mit "Freigeben" die Datei frei. Dann wird ein qm-File erzeugt, das vom QTranslator zur Laufzeit des Programms eingelesen werden kann.

Es sind halt im wesentlichen drei Zeilen mehr:

Code: Alles auswählen

[...]
translator = QtCore.QTranslator()
translator.load('de.qm')
app.installTranslator(translator)
[...]
die du benötigst. Das kannst du natürlich jetzt noch beliebig erweitern, d.h. beim Programmstart aus deinen Settings die Sprachwahl auslesen und dementsprechend das richtige qm-File auswählen. Das ist aber bei weitem einfacher als das, was du vorhast und ich sehe hierbei auch keinen wirklichen Lerneffekt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

EmaNymton hat geschrieben:Hab dir mal ein Beispiel gebastelt, damit du siehst, das Internationalisierung kein Hexenwerk ist und schon sehr bequem umgesetzt werden kann:
https://github.com/Cermit/snippets/tree ... 8n_example

Vorgehen:
Du erzeugst deinen Quelltext (hier main.py). Alle Strings, die übersetzt werden sollen, setzt du in self.tr().
Du legst ein pro-File (hier project.pro) an, in dem die Dateien aufgeführt werden, in denen Strings zur Übersetzung vorgesehen sind.
Du benutzt das CLI-Tool

Code: Alles auswählen

pylupdate5 project.pro
bzw

Code: Alles auswählen

pylupdate4 project.pro
(je nach verwendeter PyQt-Version) um die ts-Files zu erzeugen (sind nichts anderes als xml-Files).
Du benutzt den Qt Linguist, um die Übersetzungen anzugeben und gibst zum Schluss dort mit "Freigeben" die Datei frei. Dann wird ein qm-File erzeugt, das vom QTranslator zur Laufzeit des Programms eingelesen werden kann.

Es sind halt im wesentlichen drei Zeilen mehr:

Code: Alles auswählen

[...]
translator = QtCore.QTranslator()
translator.load('de.qm')
app.installTranslator(translator)
[...]
die du benötigst. Das kannst du natürlich jetzt noch beliebig erweitern, d.h. beim Programmstart aus deinen Settings die Sprachwahl auslesen und dementsprechend das richtige qm-File auswählen. Das ist aber bei weitem einfacher als das, was du vorhast und ich sehe hierbei auch keinen wirklichen Lerneffekt.
Hallo EmaNymton,

besten Dank für deine kleine Demonstration. Aber eine Frage: Wie kann ich dein kleines Beispiel herunterladen? Muss ich mich erst auf der Seite anmelden? Und dann hätte ich eines anzumerken. Wenn ich mir das alles so durchlese, was ich machen muss, dann schlage ich mir die Hände über meinem Kopf zusammen. Da sind ja ganze 4-5 Schritte die ich machen muss, bis ich am Ende erstmal eine *.qm Datei habe. In meinem Vorgang habe ich Sprachmodule, muss nicht erst 5 Schritte durchlaufen, bis ich am Ende eine *.qm Datei habe. Ich würde mir gern dein Code ansehen, aber überzeugt von den ganzen umständlichen Schritten bin ich immer noch nicht. Da habe ich in meinem Vorgang nur einen einzigen Schritt.
BlackJack

@Sophus: Das mit den Qt-Linguist unterscheidet sich von deinem Ansatz. Das ist nämlich für den Programmierer und für den Übersetzer einfacher. Aber mach's ruhig kompliziert wenn Du unbedingt willst.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

BlackJack hat geschrieben:@Sophus: Das mit den Qt-Linguist unterscheidet sich von deinem Ansatz. Das ist nämlich für den Programmierer und für den Übersetzer einfacher. Aber mach's ruhig kompliziert wenn Du unbedingt willst.
Hallo BlackJack, konntest du mein kleines Projekt zum Laufen bringen und kannst du mir bezüglich meines Ausgangsproblem helfen?
BlackJack

@Sophus: Da hatte ich doch schon etwas zu geschrieben: `QDialog` ist ein „top-level only”-Widget. Das kann man nur als eigenes Fenster verwenden und nicht als Widget in andere Widgets einbauen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

BlackJack hat geschrieben:@Sophus: Da hatte ich doch schon etwas zu geschrieben: `QDialog` ist ein „top-level only”-Widget. Das kann man nur als eigenes Fenster verwenden und nicht als Widget in andere Widgets einbauen.
Ach so, das heißt konkret für mich, dass ich aus QDialog nun QWidget machen muss, richtig? Aber selbst dann schließt sich das Programm leider nicht, sondern die Widgets auf der Form verschwinden.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Selbst 'destroy()' will auch nicht ganz funktionieren. Wie gesagt, ich habe aus QDialog nun QWidget gemacht, in der Hoffnung, dass man mit einem QWidget in einem MdiArea-Widget arbeiten kann. Aber irgendwie scheint es nicht ganz zu funktionieren. Woran könnte es denn liegen?
BlackJack

@Sophus: Kommando zurück. Anscheinend funktioniert es auch mit `QDialog`-Exemplaren. Allerdings muss man schon das richtige Objekt schliessen. Nämlich das `QMdiSubWindow`-Exemplar das zu dem `QMdiArea`-Exemplar hinzugefügt wird. Wenn man nur das Widget zerstört welches *in* diesem `QMdiSubWindow`-Exemplar angezeigt wird, dann wird halt auch nur der Inhalt von dem Fenster zerstört.

Wobei: Kommando zurück wieder zurück: Auch wenn `QDialog` funktioniert, würde ich das nur verwenden wenn man tatsächlich irgend etwas von diesem speziellen Widget verwendet. Wenn ein `QWidget`-Exemplar reicht, muss man es ja nicht unnötig kompliziert machen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

BlackJack hat geschrieben:@Sophus: Kommando zurück. Anscheinend funktioniert es auch mit `QDialog`-Exemplaren. Allerdings muss man schon das richtige Objekt schliessen. Nämlich das `QMdiSubWindow`-Exemplar das zu dem `QMdiArea`-Exemplar hinzugefügt wird. Wenn man nur das Widget zerstört welches *in* diesem `QMdiSubWindow`-Exemplar angezeigt wird, dann wird halt auch nur der Inhalt von dem Fenster zerstört.

Wobei: Kommando zurück wieder zurück: Auch wenn `QDialog` funktioniert, würde ich das nur verwenden wenn man tatsächlich irgend etwas von diesem speziellen Widget verwendet. Wenn ein `QWidget`-Exemplar reicht, muss man es ja nicht unnötig kompliziert machen.
Hey BlackJack, das Problem, was ich hier habe, ist, dass ich keinen Unterschied zwischen QWidget und QDialog verstehe. Da ich VB6ler bin, gibt es dort nur Formen, und die Formen kann man behandeln wie man will. Und ich habe die Formen auch in ein MDIFormular (Analog: QMdiArea) untergebracht, indem die Formen als Child behandelt wurden.

Aber nun zum Problem:
Du sagtest, ich solle das Objekt 'QMdiSubWindow' schließen, richtig? Jedoch habe ich dieses Objekt ja gar nicht. Schauen wir mal meine Funktion an:

Code: Alles auswählen

    def CreateNewMovie(self):                   # Neues Fenster für Film hinzufügen öffnen
        self.MDIChild = MyForm(self)
        self.mdiArea.addSubWindow(self.MDIChild)
        self.MDIChild.show()
        print "Dialog lädt"
Hier wird MDIChild als SubWindow in das Widget mdiArea hinzugefügt. Was genau muss ich in meinem Beispiel schließen?
BlackJack

@Sophus: `MDIChild` wird nicht als Subwindow hinzugefügt, sondern dem Subwindow als Inhalt. Schau Dir mal die Dokumentation zu `QMdiArea.addSubWindow()` an. Danach solltest Du zwei Wege kennen um an das `QMdiSubWindow`-Exemplar heran zu kommen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Also mit 'self.mdiArea.removeSubWindow(self.MDIChild)' komme ich offensichtlich auch nicht weit.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Nach langem rumgetüffel bin ich auf eine vermeintliche Lösung gestoßen, aber ich hätte gern eure Meinung.

TestDialog.py Was tat ich? Ich habe die Klasse "MyForm" unter anderem von QMdiSubWindow abgeleitet, schließlich sollten alle Child-Fenster bei QMdiArea von QMdiSubWindow abgeleitet werden. Vorher war sie nur vom QDialog und zeitweise auch nur vom QWidget abgeleitet. Klappte ja nicht.

Code: Alles auswählen

import sys
from PyQt4.QtGui import QWidget, QApplication, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QMessageBox, QTextEdit, QMdiSubWindow 
from PyQt4.QtCore import Qt

class MyForm(QMdiSubWindow, QDialog):
    def __init__(self, parent=None):
        QMdiSubWindow, QDialog.__init__(self, parent)
     [...]
MDIForm.py Hier schließe ich das Fenster mit der Methode 'removeSubWindow()', aber mit 'close()' klappt es auch wunderbar. Kann ich also getrost bei 'close()' bleiben oder sollte ich da auf etwas achten?

Code: Alles auswählen

[...]
    def CloseQWidget(self):
        self.MDIChild = MyForm(self)
        self.mdiArea.removeSubWindow(self.MDIChild)
        #self.close()
[...]
BlackJack

@Sophus: Das ist kompletter Unsinn. Wieder Programmieren durch raten statt zu verstehen was man da tut. Und bei beiden gezeigten Programmstellen hätte ich gewettet damit fällt man auf die Nase. Wenn das tatsächlich funktioniert, fällt das wohl unter die Kategorie „feak accident”. Oder Du trollst hier gerade ganz gewaltig.

Edit: Und es funkioniert auch tatsächlich *nicht*. Das Layout in dem MDI-Fenster ist kaputt und auf der Konsole erscheint auch eine entsprechende Warnung von Qt:

Code: Alles auswählen

QLayout: Attempting to add QLayout "" to MyForm "", which already has a layout
QWidget::setLayout: Attempting to set QLayout "" on MyForm "", which already has a layout
Wie die Schaltfläche vom `MyForm`-Objekt mit der gezeigten `CloseQWidget()`-Methode verbunden wird, will ich wahrscheinlich gar nicht wissen. Unsinnig ist sie allemal, sowohl der aktive Code als auch die auskommentierte Zeile.
Antworten