Auf Objekte aus einer anderen Klasse zugreifen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Kreser06
User
Beiträge: 33
Registriert: Samstag 21. Januar 2017, 11:49

Hallo zusammen,
bin ein absoluter Anfänger in Sachen Programmierung, und habe eine Frage:
bin am rumprobieren mit PyQT5. Ich versuche eine App zu schreiben.
Ich habe eine Klasse wo die Objekte/Widgets erstellt werden und verschiedene Klassen wo alles verarbeitet wird.
Ich möchte von einer Klasse aus eine QCheckBox die in einer anderen Klasse erstellt wurde auf "enebled" zu setzten. Kann aber da drauf nicht zugreifen.

Code: Alles auswählen

AttributeError: type object 'OpenedFile' has no attribute 'checkAll'
Ich hoffe habe mich verständlich ausgedrückt und hoffe auf eure hilfe. Am besten mit einem kleinen Beispiel.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du uns deine Code (ui-Datei inklusive) zeigst, können wir dir zeigen, wie‘s darin geht.

Denn wir haben kein Beispiel das so aussieht wie dein Code.
Kreser06
User
Beiträge: 33
Registriert: Samstag 21. Januar 2017, 11:49

Ok, aber nicht erschrecken, wie gesagt bin kein guter Programmierer.

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import re
import os
import time
import shutil

markerList = list()
markerListChecked = list()

class Fenster(QMainWindow):
    def __init__(self):
        super().__init__()

        self.title = 'IPTV-Playlist Converter 2.0'
        # self.i = 1

        self.openedfile = OpenedFile()
        self.sort = Sort()
        Log().createLog('Initialisiere das Programm-Fenster')

        self.initMe()

    def initMe(self):
        Log().createLog('Erstelle die Layouts für das Hauptfenster und erstelle die Widgets')
        # App-Fenster
        self.setWindowTitle(self.title)
        self.setGeometry(left, top, width, height)

        # StatusBar
        self.statusbar = self.statusBar()
        self.statusbar.setFont(smallFont)
        self.statusbar.showMessage('Erstellt von Sergej Kretschmann')

        # QWidget
        mainWidget = QWidget()
        mainWidget.setLayout(self.VBox_Final())
        self.setCentralWidget(mainWidget)

        self.show()

    def HBox_Title(self):
        Log().createLog('Erstelle den Title-Layout')
        # TITLE Label
        titleLbl = QLabel()
        titleLbl.setFont(titleFont)
        titleLbl.setText('IPTV Converter')
        titleLbl.setStatusTip('Erstellt von Sergej Kretschmann')
        # HBox 1 Title und Datum
        title_HBox = QHBoxLayout()
        title_HBox.setAlignment(Qt.AlignCenter)
        title_HBox.addWidget(titleLbl)

        return title_HBox

    def GroupBox_FileOpen(self):
        Log().createLog('Erstelle Files-GroupBox-Layout')
        fileOpenGroupBox = QGroupBox("Datei öffnen")
        fileOpenGroupBox.setFont(smallFont)
        self.filesHBox = QHBoxLayout()

        # Datei Öffnen Button
        self.fileOpenBtn = QPushButton('Öffnen')
        self.fileOpenBtn.setFont(normFont)
        self.fileOpenBtn.setStatusTip('.USERBOUQUET.TV, .M3U, .M3U8 oder .XML Datei öffnen')
        self.fileOpenBtn.clicked.connect(self.openBtnClicked)
        # Öffnen Button zu HBox hinzufügen
        self.filesHBox.addWidget(self.fileOpenBtn)
        self.filesHBox.addStretch(1)
        # GroupBox erstellen
        fileOpenGroupBox.setLayout(self.filesHBox)

        Log().createLog('Erstellen des Files-GroupBox-Layouts ist abgeschlossen')

        return fileOpenGroupBox
    # VBox mit Providern und SenderAnzahl
    def VBox_Files_SenderCounts(self, label):
        self.filesVBox.addWidget(label)
        self.HBox_Files_SenderCounts(self.filesVBox)
    # HBox mit VBox mit Providern und SenderAnzahl
    def HBox_Files_SenderCounts(self, vbox):
        self.filesHBox.addLayout(vbox)

    def GroupBox_EPG(self):
        Log().createLog('Erstelle EPG-GroupBox-Layout')
        epgGroupBox = QGroupBox("EPG-Quellen")
        epgGroupBox.setFont(smallFont)
        self.epgHBox = QHBoxLayout()

        # IPTVX.ONE
        self.iptvxoneCB = QCheckBox('iptvX.one')
        self.iptvxoneCB.setFont(normFont)
        self.iptvxoneCB.setStatusTip('EPG von iptvX.One verwenden')
        self.iptvxoneCB.setEnabled(False)
        self.epgHBox.addWidget(self.iptvxoneCB)

        # DORTMUNDEZ
        self.dortmundezCB = QCheckBox('Dortmundez')
        self.dortmundezCB.setFont(normFont)
        self.dortmundezCB.setStatusTip('EPG von Dortmundez verwenden')
        self.dortmundezCB.setEnabled(False)
        self.epgHBox.addWidget(self.dortmundezCB)

        # SHURA
        self.shuraCB = QCheckBox('Shura.tv')
        self.shuraCB.setFont(normFont)
        self.shuraCB.setStatusTip('EPG von Shura.tv verwenden')
        self.shuraCB.setEnabled(False)
        self.epgHBox.addWidget(self.shuraCB)

        # SIPTV
        self.sipTvEuCB = QCheckBox('SipTV.eu')
        self.sipTvEuCB.setFont(normFont)
        self.sipTvEuCB.setStatusTip('EPG von SipTV.eu verwenden')
        self.sipTvEuCB.setEnabled(False)
        self.epgHBox.addWidget(self.sipTvEuCB)

        epgGroupBox.setLayout(self.epgHBox)

        Log().createLog('Erstellen des EPG-GroupBox-Layouts ist abgeschlossen')

        return epgGroupBox

    def GroupBox_Marker(self):
        Log().createLog('Erstelle Marker-GroupBox-Layout')
        markerGroupBox = QGroupBox("Marker-Kategorien")
        markerGroupBox.setFont(smallFont)
        HBox_1 = QHBoxLayout()

        # Alle Marker
        self.checkAll = QCheckBox('Alle Ein/Ausblenden')
        self.checkAll.setStatusTip('Alle Kategorien Ein- bzw. Ausblenden.')
        self.checkAll.setEnabled(False)
        self.checkAll.stateChanged.connect(self.checkAllMarker)
        HBox_1.addWidget(self.checkAll)

        # Letzte Marker
        self.checkLast = QCheckBox('Zuletzt gewählte Kategorien')
        self.checkLast.setStatusTip('Auswählen der Kategorien die letztes mal gewählt wurden.')
        self.checkLast.setEnabled(False)
        self.checkLast.stateChanged.connect(self.checkLastMarker)
        HBox_1.addWidget(self.checkLast)

        # Babulja Marker
        self.checkBabulja = QCheckBox('Kategorien Babulja')
        self.checkBabulja.setStatusTip('Auswählen der Babuljka Kategorie.')
        self.checkBabulja.setEnabled(False)
        # self.checkLast.stateChanged.connect(self.checkBabuljaMarker)
        HBox_1.addWidget(self.checkBabulja)

        HBox_1.addStretch(1)

        # Marker
        self.sort.findMarker()
        markerLayout = QGridLayout()
        y = 1
        x = 0
        for i, v in enumerate(markerList):
            markerList[i] = QCheckBox(v)
            markerList[i].setStatusTip('Kategorie ' + str(v))
            markerList[i].setEnabled(False)
            if v in markerListChecked:
                markerList[i].setChecked(True)
            if x > 8:
                x = 0
                y += 1
            markerLayout.addWidget(markerList[i], y, x)
            x += 1
        VBox_Marker = QVBoxLayout()
        VBox_Marker.addLayout(HBox_1)
        VBox_Marker.addLayout(markerLayout)
        markerGroupBox.setLayout(VBox_Marker)

        Log().createLog('Erstellen des Marker-GroupBox-Layouts ist abgeschlossen')

        return markerGroupBox

    def checkAllMarker(self):
        for i, v in enumerate(markerList):
            if self.checkAll.isChecked():
                self.checkLast.setChecked(False)
                self.checkBabulja.setChecked(False)
                self.playlist.markerList[i].setChecked(True)
            else:
                self.markerList[i].setChecked(False)

    def checkLastMarker(self):
        self.getCheckedMarkerList()

        for i, v in enumerate(markerList):
            if self.checkLast.isChecked():
                self.checkAll.setChecked(False)
                if v.text() in markerListChecked:
                    markerList[i].setChecked(True)
            else:
                markerList[i].setChecked(False)

    def getCheckedMarkerList(self):
        with open('files/settings/checkedMarker.txt', 'r', encoding='utf-8') as checkedMarker:
            for zeile in checkedMarker:
                zeile = zeile.strip()
                markerListChecked.append(zeile)

    # Komplette Gui
    def VBox_Final(self):
        finalVBox = QVBoxLayout()
        finalVBox.addLayout(self.HBox_Title())
        finalVBox.addWidget(self.GroupBox_FileOpen())
        finalVBox.addWidget(self.GroupBox_EPG())
        finalVBox.addWidget(self.GroupBox_Marker())
        finalVBox.addStretch(1)

        return finalVBox

    def openBtnClicked(self):
        Log().createLog('"Öffnen"-Button wurde geklickt')
        for pfad in self.openFileNamesDialog():
            datei = pfad.split('/')
            datei = datei[-1]
            # Provider Label
            providerLbl = QLabel()
            providerLbl.setText(datei)
            providerLbl.setFont(normFont)
            providerLbl.setStatusTip(datei)
            # Anzahl Sender
            senderCountLbl = QLabel()
            senderCountLbl.setFont(smallFont)
            senderCountLbl.setText(self.openedfile.countSender(pfad, datei))
            self.filesVBox = QVBoxLayout()
            self.VBox_Files_SenderCounts(providerLbl)
            self.VBox_Files_SenderCounts(senderCountLbl)
            self.openedfile.fileCheck(datei)

        Log().createLog('Schalte den "Öffnen"-Button aus \n')
        self.fileOpenBtn.setEnabled(False)
        #self.aktionBtn.setEnabled(True)

    def openFileNamesDialog(self):
        Log().createLog('FileNameDialog wird geöffnet.')
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        pfade, _ = QFileDialog.getOpenFileNames(self, "QFileDialog.getOpenFileName()", "",
                                                  "*.tv *.m3u *.m3u8 *.xml",
                                                  options=options)
        return pfade

class OpenedFile():
    def countSender(self, pfad, datei):
        Log().createLog('Die Sender aus "' + datei + '" werden gezählt')
        c = 0
        string = ''
        with open(pfad, 'r', encoding='utf-8') as f:
            inhalt = f.readlines()
            for line in inhalt:
                if pfad.endswith('.xml'):
                    if re.search('<channel id=', line):
                        c += 1
                        string = ' ID\'s:'
                elif pfad.endswith('.tv'):
                    if re.search('http', line):
                        c += 1
                        string = ' Sender:'
                else:
                    if line.startswith('http') or line.startswith('rtmp'):
                        # if re.search('http', line):
                        c += 1
                        string = ' Sender:'
            Log().createLog(str(c) + string + '  wurden in "' + datei + '" gefunden')
            senderCount = string + str(c)

        return senderCount

    def fileCheck(self, datei):
        Log().createLog('Analysiere die "' + datei + '" und Schalte die Buttons frei')

        if datei.endswith('.xml'):
            if 'iptvxone' in datei.replace('.', '').lower():
                self.iptvxoneCB.setEnabled(True)
                self.iptvxoneCB.setChecked(True)
            if 'dortmundez' in datei.replace('.', '').lower():
                self.dortmundezCB.setEnabled(True)
                self.dortmundezCB.setChecked(True)
            if 'shura' in datei.replace('.', '').lower():
                self.shuraCB.setEnabled(True)
                self.shuraCB.setChecked(True)
            self.sipTvEuCB.setEnabled(True)
            self.sipTvEuCB.setChecked(True)
            self.chIdImport.setEnabled(True)
            self.chIdImport.setChecked(True)
        else:
            self.checkAll.setEnabled(True)
            self.checkLast.setEnabled(True)
            for i, v in enumerate(self.playlist.markerList):
                v.setEnabled(True)
        if datei.endswith('.m3u'):
            self.m3uBuild.setEnabled(True)
            self.m3u8Build.setEnabled(True)
            self.userBBuild.setEnabled(True)
        if datei.endswith('.m3u8'):
            self.m3uBuild.setEnabled(True)
            self.m3u8Build.setEnabled(True)
            self.userBBuild.setEnabled(True)
        if datei.endswith('.tv'):
            self.m3uBuild.setEnabled(True)
            self.m3u8Build.setEnabled(True)
            self.userBBuild.setEnabled(True)

        if self.fileCount(self.pfade) > 1:
            self.m3uAlle.setEnabled(True)
            self.m3uEin.setEnabled(True)
            self.m3u8Alle.setEnabled(True)
            self.m3u8Ein.setEnabled(True)

        self.checkStatus()

class Sort:
    def findMarker(self):

        with open('files/sortierung.txt', 'r', encoding='utf-8-sig') as sortierung:
            inhalt = sortierung.readlines()
            for line in inhalt:
                z = line.strip().split(';')
                marker = z[0].lstrip()
                if not marker in markerList:
                    markerList.append(marker)

        return markerList



class Log:
    def createLog(self, text):
        print(QDateTime.currentDateTime().toString("dd.MM.yyyy hh:mm:ss.zzz") + ' --- ' + text)

app = QApplication(sys.argv)
# Font
titleFont = QFont('Helvetica', 20, QFont.ExtraBold, italic=True)
normFont = QFont('Arial', 13)
normFettFont = QFont('Helvetica', 13, QFont.ExtraBold)
smallFont = QFont('Helvetica', 10)
# Display Size
screen_rect = app.desktop().screenGeometry()
screen_width, screen_height = screen_rect.width(), screen_rect.height()
# Window Size/Place
width = int(screen_width * 0.8)
height = int(screen_height * 0.8)
left = int((screen_width - width) / 2)
top = int((screen_height - height) / 2)
ex = Fenster()
sys.exit(app.exec_())
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Kreser06: `OpenedFile` hat ganz offensichtlich nicht die Attribute auf die in `fileCheck()` zugegriffen werden soll.

`OpenedFile`, `Sort`, und `Log` sind auch überhaupt gar keine Klassen. Da sind einfach nur Funktionen sinnloserweise in Klassen gesteckt worden. Lass das. Python ist nicht Java, es gibt Funktionen, und man muss nicht alles zwanghaft in Klassen stecken.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Sternchen-Importe sind Böse™. Das macht Programme unnötig unübersichtlicher und fehleranfälliger und es besteht die Gefahr von Namenskollisionen.

Nur einer der `enumerate()`-Aufrufe macht Sinn, weil nur bei einem `i` tatsächlich benötigt wird. Besonders verwirrend sind die Schleifen wo dann sowohl auf das Element über die Schleifenvariable *und* über den Index zugegriffen wird. Warum? Wonach entscheidest Du wann welcher Weg genommen wird?

Edit: Zum Logging: Sowohl Python als auch Qt haben da etwas für. Wenn man mit beidem nicht zufrieden ist, verwendet man eine der vorhandenen externen Logging-Bibliotheken. Das letzte was man macht ist noch so etwas selber zu basteln.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Kreser06
User
Beiträge: 33
Registriert: Samstag 21. Januar 2017, 11:49

Vielen Dank für die Aufklärung.
`OpenedFile`, `Sort`, und `Log` sind auch überhaupt gar keine Klassen. Da sind einfach nur Funktionen sinnloserweise in Klassen gesteckt worden. Lass das. Python ist nicht Java, es gibt Funktionen, und man muss nicht alles zwanghaft in Klassen stecken.
Ich wollte Klassen verwenden übersichtshalber, der Code wird nämlich sehr lang.
Nur einer der `enumerate()`-Aufrufe macht Sinn, weil nur bei einem `i` tatsächlich benötigt wird. Besonders verwirrend sind die Schleifen wo dann sowohl auf das Element über die Schleifenvariable *und* über den Index zugegriffen wird. Warum? Wonach entscheidest Du wann welcher Weg genommen wird?
Das Stimmt, Danke. Ich mache nur viel mit copy/paste und manches passe ich etwas an, deswegen geht das oft unter. Daran werde ich arbeiten.
Edit: Zum Logging: Sowohl Python als auch Qt haben da etwas für. Wenn man mit beidem nicht zufrieden ist, verwendet man eine der vorhandenen externen Logging-Bibliotheken. Das letzte was man macht ist noch so etwas selber zu basteln.
Ok, das wusste ich nicht, habe das ja auch nur für debug eingebaut, damit ich sehen kann was das Programm macht.


Aber was meinst du zum GUI Aufbau selbst? Kann man das so lassen, oder hast du paar tips für mich, wie ich das einfacher bzw. übersichtlicher machen kann?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Kreser06: Was ist an vollkommen sinnfreien Klassen die semantisch gar keine Klassen sind bitte übersichtlicher als das einfacher mit Funktionen auszudrücken was letztlich einfach nur Funktionen sind?

Ich dachte Du hast mit den Punkten erst einmal genug zu tun. Insbesondere das beseitigen der globalen Variablen wird wichtige Änderungen nach sich ziehen.

Zu der `Fenster`-Klasse fällt auf das `initMe()` überflüssig ist — das sollte einfach in der `__init__()` stehen. Aus der Methode zur Initialisierung eine Methode zur Initialisierung aufzurufen macht keinen Sinn.

Das `title`-Attribut wird nirgends wirklich benutzt. Um die Zeichenkette als Fenstertitel zu setzen muss die kein Attribut sein.

Der `setGeometry()`-Aufruf ist etwas wofür Benutzer Dich leidenschaftlich hassen werden. Fenster die besser wissen wollen wo und wie gross exakt sie dargestellt werden wollen als die Fensterverwaltung oder der Benutzer sind IMHO die Pest.

Dem Benutzer Schriftarten- und -grössen vorzuschreiben ist noch fieser. Die stellt der Benutzer in seinem System Aufgrund der Bildschirmgrösse und -auflösung, und seinen Bedürfnissen ein. Da kommt man als Anwendungsentwickler nicht daher und setzt dem Benutzer Schriftarten und -grössen vor die vielleicht deutlich von den Systemeinstellungen abweichen. Man kann hier und da eingestellten Schriftarten abfragen und relativ verändern, aber nicht einfach durch absolute, andere Schriften ersetzen.

Zur Namensschreibweise habe ich ja schon etwas geschrieben. Qt hält sich da nicht dran weil die Namen von einer C++-Bibliothek kommen, aber auch an die Qt-Namenschreibweise halten sich Deine Namen nicht alle. Du hast da sehr kreative Mischungen. Das sollte nicht sein.

Verwende keine kryptischen Abkürungen. Wenn man `label` meint, sollte man nicht nur `lbl` schreiben. `v` schon gar nicht für — ja was eigentlich?

Grunddatentypen haben in Namen nichts zu suchen. Das ändert man öfter mal, und dann muss man überall im Quelltext alle betroffenen Namen ändern, oder man hat irreführende, falsche Namen im Programm.

Funktionen und Methoden werden üblicherweise nach der Tätigkeit benannt, die die Funktion oder Methode ausführt.

`HBox_Title()` ist keine Methode sondern eine Funktion die in die Klasse gesteckt wurde. Der Code gehört auch eher einfach in die `__init__()`.

Der `show()`-Aufruf gehört nicht in die `__init__()`. Widgets zeigen sich nicht selbst an nur weil sie erstellt werden. Das macht kein vorhandenes Widget, also sollte man damit in eigenen, abgeleiteten nicht anfangen.

Nachdem die `__init__()` abgelaufen ist, sollte das Objekt komplett und in einem benutzbaren Zustand sein. Es werden keine neuen Attribute in anderen Methoden eingeführt.

XML verarbeitet man nicht mit Zeichenkettenoperationen und regulären Ausdrücken sondern mit einem XML-Parser.

Es macht Sinn die Geschäftslogik und die GUI zu trennen.

So, das sollte jetzt aber wirklich erst einmal genug Stoff zum umsetzen sein.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Kreser06
User
Beiträge: 33
Registriert: Samstag 21. Januar 2017, 11:49

Vielen Dank, dann habe ich ja was vor mir :)
Kreser06
User
Beiträge: 33
Registriert: Samstag 21. Januar 2017, 11:49

Hallo noch mal Allerseits,

habe mir zum üben einen anderen Code geschrieben und habe immer noch Probleme von einer Klasse auf die andere zuzugreifen. Ich habe versucht so gut es ging die Tipps von __blackjack__ umzusetzen, und hoffe das es lesbar ist.

Zum Code:
Das Programm soll einfach die Stromkosten berechnen, in dem erst unter Einstellungen(Sub Fenster) die Ganzen Beträge wie Grundgebühr, kosten pro KWh usw. einträgt, wenn man damit Fertig ist klickt man auf Speichern, dann werden die Daten gespeichert(habe noch nicht im Code, aber ist erst mal nicht so wichtig) und das Fenster schliesst sich.

Dann trägt man für jeden Monat den Zählerstand ein und klickt auf Berechnen. Dabei müssen die unter Einstellungen eingetragenen Daten dem entsprechend berechnet werden. In der calculate.py habe ich dazu was geschrieben.
Natürlich muss das ganze auch gespeichert werden und im nächsten Monat, wenn man das Programm aufmacht, sollen die Daten im Programm stehen.

Kann mir vielleicht jemand helfen den Code fertigzustellen? Mir geht es hauptsächlich darum, zu verstehen wie man von einer Klasse auf die Objekte der anderen Klasse zugreift und Text/Wert ausliest, oder die Objekte selbst ändert z.B. Die Farbe der Schrift oder ähnliches.

Und Hier der Code: (diesmal habe ich versucht alles zu trennen, deswegen sind es drei Dateien)
StromBerechnung.py

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication

import MainWindow

if __name__ == '__main__':
	app = QApplication(sys.argv)

	window = MainWindow.MainWindow()
	window.show()

	sys.exit(app.exec_())
MainWindow.py

Code: Alles auswählen

# -*- coding: utf-8 -*-
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QGroupBox, QHBoxLayout, QLabel, QLineEdit,QPushButton,
                             QVBoxLayout, QMainWindow, QWidget)

from settings import Settings

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

        ## Set Window Properties
        self.setWindowTitle("Stromkosten Berechnung")
        self.setMinimumWidth(300)
        self.setMinimumHeight(450)

        self.settingsDialog = Settings(self)

        self.create_month_box()
        self.create_buttons_box()
        self.create_result_box()

        mainLayout = QVBoxLayout()

        mainLayout.addWidget(self.month_groupBox)
        mainLayout.addStretch(1)
        mainLayout.addWidget(self.result_groupbox)
        mainLayout.addWidget(self.buttons_groupbox)

        # QWidget-------------------------------------------------------------------------------------------------------
        mainwidget = QWidget(self)
        self.setCentralWidget(mainwidget)
        mainwidget.setLayout(mainLayout)

    def create_month_box(self):
        months = ["Januar", "Februar", "März", "April", "Mai", "Juni",
                 "Juli", "August", "September", "Oktober", "November", "Dezember"]
        mask = "999999,99 KW\h"

        self.month_groupBox = QGroupBox("Zählerstand eintragen")
        vbox = QVBoxLayout()

        for month in months:
            hbox = QHBoxLayout()

            month_lbl = QLabel(month)
            month_lbl.setFixedWidth(70)
            month_lbl.setAlignment(Qt.AlignRight)

            consumption_lbl = QLineEdit()
            consumption_lbl.setInputMask(mask)
            consumption_lbl.setObjectName(month[0:3])
            consumption_lbl.setFixedWidth(110)

            costs_lbl = QLabel("0")
            costs_lbl.setAlignment(Qt.AlignRight)

            currency_lbl = QLabel("€")

            hbox.addWidget(month_lbl)
            hbox.addWidget(consumption_lbl)
            hbox.addStretch(1)
            hbox.addWidget(costs_lbl)
            hbox.addWidget(currency_lbl)

            vbox.addLayout(hbox)

        self.month_groupBox.setLayout(vbox)

    def create_result_box(self):
        self.result_groupbox = QGroupBox()
        hbox1 = QHBoxLayout()
        hbox2 = QHBoxLayout()
        hbox3 = QHBoxLayout()
        vbox = QVBoxLayout()

        calculated_lbl = QLabel("Berechnete Kosten:")
        calculated_result_lbl = QLabel("0")

        hbox1.addWidget(calculated_lbl)
        hbox1.addWidget(calculated_result_lbl)

        paid_lbl = QLabel("Bereits gezahlt:")
        paid_result_lbl = QLabel("0")

        hbox2.addWidget(paid_lbl)
        hbox2.addWidget(paid_result_lbl)

        difference_lbl = QLabel("Differenz:")
        difference_result_lbl = QLabel("0")

        hbox3.addWidget(difference_lbl)
        hbox3.addWidget(difference_result_lbl)

        vbox.addLayout(hbox1)
        vbox.addLayout(hbox2)
        vbox.addLayout(hbox3)

        self.result_groupbox.setLayout(vbox)

    def create_buttons_box(self):
        self.buttons_groupbox = QGroupBox()
        hbox = QHBoxLayout()

        quit_btn = QPushButton("Schließen")
        quit_btn.clicked.connect(self.quit_clicked)

        calc_btn = QPushButton("Berechnen")
        calc_btn.clicked.connect(self.calc_clicked)

        settings_btn = QPushButton("Einstellungen")
        settings_btn.clicked.connect(self.settings_clicked)

        hbox.addWidget(quit_btn)
        hbox.addWidget(calc_btn)
        hbox.addWidget(settings_btn)

        self.buttons_groupbox.setLayout(hbox)

    def quit_clicked(self):
        print("Exit")
        sys.exit()

    def settings_clicked(self):
        print("Einstellungen")
        self.settingsDialog.show()

    def calc_clicked(self):
        print("Calculate")
        pass

settings.py

Code: Alles auswählen

# -*- coding: utf-8 -*-

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QGroupBox, QHBoxLayout, QLabel, QLineEdit,QPushButton,
                             QVBoxLayout, QMainWindow, QWidget)

class Settings(QMainWindow):
    def __init__(self, parent=None):
        super(Settings, self).__init__(parent)

        ## Set Window Properties
        self.setWindowTitle("Einstellungen")
        self.setMinimumWidth(300)
        self.setMinimumHeight(400)

        self.create_input_box()
        self.create_butons_box()

        mainLayout = QVBoxLayout()

        mainLayout.addWidget(self.groupbox)
        mainLayout.addWidget(self.button_box)

        mainwidget = QWidget(self)
        self.setCentralWidget(mainwidget)
        mainwidget.setLayout(mainLayout)

    def create_input_box(self):
        labels = ["Grundgebühr", "Kosten pro KWh", "Bonusrückzahlungen", "Monatlicher Abschlag"]
        vbox = QVBoxLayout()
        self.groupbox = QGroupBox("Daten eintragen")

        for label in labels:
            hbox = QHBoxLayout()

            basic_charge_lbl = QLabel(label)
            basic_charge_lbl.setAlignment(Qt.AlignRight)
            basic_charge_lbl.setFixedWidth(140)

            if "KWh" in label:
                basic_charge_lineEdit = QLineEdit()
                basic_charge_lineEdit.setInputMask("99,9999")
                basic_charge_lineEdit.setFixedWidth(60)
                basic_charge_lineEdit.setObjectName(label[0:3])

                currency_lbl = QLabel("ct")
            else:
                basic_charge_lineEdit = QLineEdit()
                basic_charge_lineEdit.setInputMask("999,99")
                basic_charge_lineEdit.setFixedWidth(60)
                basic_charge_lineEdit.setObjectName(label[0:3])

                currency_lbl = QLabel("€")

            hbox.addWidget(basic_charge_lbl)
            hbox.addWidget(basic_charge_lineEdit)
            hbox.addWidget(currency_lbl)

            vbox.addLayout(hbox)
        vbox.addStretch(1)

        self.groupbox.setLayout(vbox)

    def create_butons_box(self):
        self.button_box = QGroupBox()
        hbox = QHBoxLayout()

        save_btn = QPushButton("Speichern")
        save_btn.clicked.connect(self.save_clicked)

        hbox.addWidget(save_btn)

        self.button_box.setLayout(hbox)

    def save_clicked(self):
        self.hide()


calculate.py

Code: Alles auswählen

# -*- coding: utf-8 -*-

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QGroupBox, QHBoxLayout, QLabel, QLineEdit,QPushButton,
                             QVBoxLayout, QMainWindow, QWidget)

class Settings(QMainWindow):
    def __init__(self, parent=None):
        super(Settings, self).__init__(parent)

        ## Set Window Properties
        self.setWindowTitle("Einstellungen")
        self.setMinimumWidth(300)
        self.setMinimumHeight(400)

        self.create_input_box()
        self.create_butons_box()

        mainLayout = QVBoxLayout()

        mainLayout.addWidget(self.groupbox)
        mainLayout.addWidget(self.button_box)

        mainwidget = QWidget(self)
        self.setCentralWidget(mainwidget)
        mainwidget.setLayout(mainLayout)

    def create_input_box(self):
        labels = ["Grundgebühr", "Kosten pro KWh", "Bonusrückzahlungen", "Monatlicher Abschlag"]
        vbox = QVBoxLayout()
        self.groupbox = QGroupBox("Daten eintragen")

        for label in labels:
            hbox = QHBoxLayout()

            basic_charge_lbl = QLabel(label)
            basic_charge_lbl.setAlignment(Qt.AlignRight)
            basic_charge_lbl.setFixedWidth(140)

            if "KWh" in label:
                basic_charge_lineEdit = QLineEdit()
                basic_charge_lineEdit.setInputMask("99,9999")
                basic_charge_lineEdit.setFixedWidth(60)
                basic_charge_lineEdit.setObjectName(label[0:3])

                currency_lbl = QLabel("ct")
            else:
                basic_charge_lineEdit = QLineEdit()
                basic_charge_lineEdit.setInputMask("999,99")
                basic_charge_lineEdit.setFixedWidth(60)
                basic_charge_lineEdit.setObjectName(label[0:3])

                currency_lbl = QLabel("€")

            hbox.addWidget(basic_charge_lbl)
            hbox.addWidget(basic_charge_lineEdit)
            hbox.addWidget(currency_lbl)

            vbox.addLayout(hbox)
        vbox.addStretch(1)

        self.groupbox.setLayout(vbox)

    def create_butons_box(self):
        self.button_box = QGroupBox()
        hbox = QHBoxLayout()

        save_btn = QPushButton("Speichern")
        save_btn.clicked.connect(self.save_clicked)

        hbox.addWidget(save_btn)

        self.button_box.setLayout(hbox)

    def save_clicked(self):
        self.hide()



Ich hoffe ihr könnt mir helfen.
Vielen Dank im voraus.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Kreser06: In `MainWindow.__init__()` wird das `parent`-Argument gar nicht verwendet.

Sich mit `setFixedWidth()` Grid- oder Form-Layouts nachzubasteln ist kaputt.

In `create_result_box()` gibt es durchnummerierte Namen die nicht sein sollten.

`sys.exit()` beendet den gesamten Prozess ohne auf den normalen Ablauf von Qt Rücksicht zu nehmen. Man würde hier einfach nur das Fenster schliessen. Wenn alle Fenster geschlossen sind, dann beendet Qt von selbst seine Hauptschleife.

Beide Fenster verwenden überhaupt gar keine Eigenschaft von `QMainWindow`, da reicht `QWidget` aus.

Das ganze mal ein bisschen eingedampft:

Code: Alles auswählen

#!/usr/bin/env python3
import sys

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
    QApplication,
    QFormLayout,
    QGridLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QPushButton,
    QVBoxLayout,
    QWidget,
)


class SettingsDialog(QWidget):
    def __init__(self):
        super().__init__(windowTitle="Einstellungen")
        main_layout = QVBoxLayout()

        groupbox = QGroupBox("Daten eintragen")
        layout = QFormLayout()
        label_names = [
            "Grundgebühr",
            "Kosten pro KWh",
            "Bonusrückzahlungen",
            "Monatlicher Abschlag",
        ]
        for label_name in label_names:
            layout.addRow(
                label_name,
                QLineEdit(
                    inputMask=(
                        "99,9999 ct" if "KWh" in label_name else "999,99 €"
                    )
                ),
            )
        groupbox.setLayout(layout)
        main_layout.addWidget(groupbox)

        layout = QHBoxLayout()
        save_button = QPushButton("Speichern")
        layout.addWidget(save_button)
        main_layout.addLayout(layout)

        self.setLayout(main_layout)

        save_button.clicked.connect(self.hide)


class MainWindow(QWidget):
    MONTH_NAMES = [
        "Januar",
        "Februar",
        "März",
        "April",
        "Mai",
        "Juni",
        "Juli",
        "August",
        "September",
        "Oktober",
        "November",
        "Dezember",
    ]

    def __init__(self, parent=None):
        super().__init__(parent, windowTitle="Stromkosten Berechnung")
        self.settings_dialog = SettingsDialog()

        main_layout = QVBoxLayout()

        layout = QGridLayout()
        for i, month_name in enumerate(self.MONTH_NAMES):
            layout.addWidget(QLabel(month_name), i, 0, Qt.AlignRight)
            layout.addWidget(QLineEdit(inputMask="999999,99 KWh"), i, 1)
            layout.addWidget(QLabel("0"), i, 2, Qt.AlignRight)
            layout.addWidget(QLabel("€"), i, 3, Qt.AlignRight)

        group_box = QGroupBox("Zählerstand eintragen")
        group_box.setLayout(layout)
        main_layout.addWidget(group_box)

        groupbox = QGroupBox()
        layout = QVBoxLayout()
        for text in ["Berechnete Kosten", "Bereits gezahlt", "Differenz"]:
            row_layout = QHBoxLayout()
            row_layout.addWidget(QLabel(text + ":"))
            row_layout.addWidget(QLabel("0"))
            layout.addLayout(row_layout)

        groupbox.setLayout(layout)
        main_layout.addWidget(groupbox)

        layout = QHBoxLayout()

        quit_button = QPushButton("Schließen")
        layout.addWidget(quit_button)

        calc_button = QPushButton("Berechnen")
        layout.addWidget(calc_button)

        settings_button = QPushButton("Einstellungen")
        layout.addWidget(settings_button)

        main_layout.addLayout(layout)

        self.setLayout(main_layout)

        quit_button.clicked.connect(self.close)
        calc_button.clicked.connect(lambda: print("Calculate"))
        settings_button.clicked.connect(self.settings_dialog.show)


def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
Wobei ich die Eingaben mit den Masken sehr schwer zu verwenden finde. Eine `QDoubleSpinBox` ist für solche Werte sicher besser geeignet als ein Texteingabefeld.

Der `SettingsDialog` könnte davon profitieren von `QDialog` zu erben, weil dann vom Hauptfenster aus die Signale von `QDialog` verwendet werden können.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten