Portierung eines Beispielprogramms von Tkinter nach PyQt5

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Frage 1 mit dem Problem des nicht erscheinenden Vollbild-Fensters ist mittlerweile gelöst. Nach einem Hinweis hat mir wohl zum ersten Mal der Garbage Collector Probleme bereitet.

Inzwischen habe ich mein Beispielprogramm vervollständigt.
  1. Was haltet ihr von dem folgenden Code?
  2. Warum bleiben die Elemente im dictionary `self.COLORS` stets in der selben Reihenfolge geordnet, obwohl ein dictionary doch ungeordnet sein soll?
  3. Im Qt Designer hatte der Rahmen des Hauptfensters noch einen gewissen Abstand zu den Widgets, aber im fertigen Programm reichen die Widgets bis zum Rand des Hauptfensters. Warum ist das so?
Hauptfenster im Qt Designer:
Bild

Hauptfenster vom fertigen Programm:
Bild


main.py:

Code: Alles auswählen

import sys
import time
from random import choice
from statistics import mean

from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog
from PyQt5.uic import loadUi
from PyQt5 import QtCore


class ConcentrationTest(object):
    def __init__(self, COLORS, designated_color):
        self.COLORS = COLORS
        self.designated_color = designated_color
        self.start_time = None
        self.color = None
        self.color_locked = True
        self.reaction_times = []
        self.false_positives = 0
        self.color_missed = 0
        self.key_too_often_pressed = 0

    def space_pressed(self):
        if self.color == self.designated_color:
            if self.color_locked:
                self.key_too_often_pressed += 1
            else:
                reaction_time = time.monotonic() - self.start_time
                self.reaction_times.append(reaction_time)
                self.color_locked = True
        else:
            self.false_positives += 1

    def next_round(self):
        if not self.color_locked and self.color == self.designated_color:
            self.color_missed += 1
        self.color_locked = False
        current_color = self.color
        while self.color == current_color:
            self.color = choice(list(self.COLORS.values()))
        self.start_time = time.monotonic()  # Ereignis Timer
        return self.color


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__()
        loadUi("mainwindow.ui", self)
        self.COLORS = {"Schwarz": QtCore.Qt.black,
                       "Blau": QtCore.Qt.blue,
                       "Cyan": QtCore.Qt.cyan,
                       "Grün": QtCore.Qt.green,
                       "Magenta": QtCore.Qt.magenta,
                       "Rot": QtCore.Qt.red,
                       "Gelb": QtCore.Qt.yellow}
        self.comboBox_designated_color.addItems(self.COLORS)
        self.pushButton_start_test.clicked.connect(self.show_concentration_test_window)

    def get_designated_color(self):
        return self.COLORS[self.comboBox_designated_color.currentText()]

    def get_switch_time(self):
        return self.spinBox_switch_time.value()

    def show_concentration_test_window(self):
        self.concentration_test = ConcentrationTest(self.COLORS, self.get_designated_color())
        self.concentration_test_window = ConcentrationTestWindow(self.concentration_test, self.get_switch_time())
        self.concentration_test_window.show()
        self.concentration_test_window.change_screen_color()


class ConcentrationTestWindow(QMainWindow):
    def __init__(self, concentration_test, switch_time, parent=None):
        super().__init__(parent)
        self.showFullScreen()
        self.concentration_test = concentration_test
        self.switch_time = switch_time
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.change_screen_color)
        self.pal = self.palette()
        self.setAutoFillBackground(True)

    def change_screen_color(self):
        color = self.concentration_test.next_round()
        self.pal.setColor(self.backgroundRole(), color)
        self.setPalette(self.pal)
        self.timer.start(self.switch_time)

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Escape:
            self.show_result()

        if event.key() == QtCore.Qt.Key_Space:
            self.concentration_test.space_pressed()

    def show_result(self):
        self.close()
        self.results_window = ResultsWindow(self.concentration_test)
        self.results_window.show()



class ResultsWindow(QDialog):
    def __init__(self, concentration_test, parent=None):
        super().__init__(parent)
        loadUi("resultswindow.ui", self)
        self.concentration_test = concentration_test
        self.pushButton_OK.clicked.connect(self.reject)

        self.label_result_too_often.setText(str(self.concentration_test.key_too_often_pressed))
        self.label_result_color_missed.setText(str(self.concentration_test.color_missed))
        self.label_result_false_positives.setText(str(self.concentration_test.false_positives))
        if self.concentration_test.reaction_times:
            self.mean_reaction_time = round(mean(self.concentration_test.reaction_times), 5)
            self.label_result_mean_reaction_time.setText(str(self.mean_reaction_time) + " s")
            self.listWidget_result_reaction_times.addItems([str(i) + " s" for i in self.concentration_test.reaction_times])
        else:
            self.label_mean_reaction_time.setDisabled(True)
            self.label_reaction_times.setDisabled(True)
            self.listWidget_result_reaction_times.setDisabled(True)


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


if __name__ == "__main__":
    main()

mainwindow.ui:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>350</width>
    <height>257</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Farbkonzentrationstest</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout_2">
    <item row="0" column="0">
     <layout class="QVBoxLayout" name="verticalLayout">
      <property name="leftMargin">
       <number>0</number>
      </property>
      <property name="topMargin">
       <number>0</number>
      </property>
      <item>
       <spacer name="verticalSpacer_2">
        <property name="orientation">
         <enum>Qt::Vertical</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
          <height>40</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QGroupBox" name="groupBox">
        <layout class="QGridLayout" name="gridLayout">
         <item row="1" column="1">
          <widget class="QSpinBox" name="spinBox_switch_time">
           <property name="maximum">
            <number>100000</number>
           </property>
           <property name="value">
            <number>1000</number>
           </property>
          </widget>
         </item>
         <item row="1" column="0">
          <widget class="QLabel" name="label_switch_time">
           <property name="text">
            <string>Umschaltzeit (ms):</string>
           </property>
           <property name="alignment">
            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
           </property>
          </widget>
         </item>
         <item row="0" column="1">
          <widget class="QComboBox" name="comboBox_designated_color">
           <property name="currentText">
            <string/>
           </property>
          </widget>
         </item>
         <item row="0" column="0">
          <widget class="QLabel" name="label_designated_color">
           <property name="text">
            <string>Zielfarbe:</string>
           </property>
           <property name="alignment">
            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
           </property>
          </widget>
         </item>
        </layout>
       </widget>
      </item>
      <item>
       <spacer name="verticalSpacer">
        <property name="orientation">
         <enum>Qt::Vertical</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
          <height>40</height>
         </size>
        </property>
       </spacer>
      </item>
      <item>
       <widget class="QPushButton" name="pushButton_start_test">
        <property name="layoutDirection">
         <enum>Qt::LeftToRight</enum>
        </property>
        <property name="text">
         <string>Test starten</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>350</width>
     <height>28</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>
resultswindow.ui:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog_results</class>
 <widget class="QDialog" name="Dialog_results">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>700</width>
    <height>350</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Ergebnisse</string>
  </property>
  <layout class="QGridLayout" name="gridLayout_2">
   <item row="0" column="0">
    <layout class="QVBoxLayout" name="verticalLayout">
     <item>
      <widget class="QGroupBox" name="groupBox">
       <layout class="QGridLayout" name="gridLayout">
        <item row="3" column="1">
         <widget class="QLabel" name="label_result_mean_reaction_time">
          <property name="text">
           <string/>
          </property>
         </widget>
        </item>
        <item row="1" column="0">
         <widget class="QLabel" name="label_color_missed">
          <property name="text">
           <string>Bei richtiger Farbe nicht gedrückt:</string>
          </property>
          <property name="alignment">
           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
          </property>
         </widget>
        </item>
        <item row="2" column="1">
         <widget class="QLabel" name="label_result_false_positives">
          <property name="text">
           <string/>
          </property>
         </widget>
        </item>
        <item row="0" column="1">
         <widget class="QLabel" name="label_result_too_often">
          <property name="text">
           <string/>
          </property>
         </widget>
        </item>
        <item row="4" column="0">
         <widget class="QLabel" name="label_reaction_times">
          <property name="text">
           <string>Reaktionszeit pro richtig gedrückter Farbe:</string>
          </property>
          <property name="alignment">
           <set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>
          </property>
         </widget>
        </item>
        <item row="2" column="0">
         <widget class="QLabel" name="label_false_positives">
          <property name="text">
           <string>Bei falscher Farbe gedrückt:</string>
          </property>
          <property name="alignment">
           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
          </property>
         </widget>
        </item>
        <item row="1" column="1">
         <widget class="QLabel" name="label_result_color_missed">
          <property name="text">
           <string/>
          </property>
         </widget>
        </item>
        <item row="3" column="0">
         <widget class="QLabel" name="label_mean_reaction_time">
          <property name="text">
           <string>Gemittelte Reaktionszeit:</string>
          </property>
          <property name="alignment">
           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
          </property>
         </widget>
        </item>
        <item row="0" column="0">
         <widget class="QLabel" name="label_too_often">
          <property name="text">
           <string>Bei richtiger Farbe zu oft gedrückt:</string>
          </property>
          <property name="alignment">
           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
          </property>
         </widget>
        </item>
        <item row="4" column="1">
         <widget class="QListWidget" name="listWidget_result_reaction_times">
          <property name="minimumSize">
           <size>
            <width>250</width>
            <height>0</height>
           </size>
          </property>
          <property name="maximumSize">
           <size>
            <width>16777215</width>
            <height>100</height>
           </size>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
     </item>
     <item>
      <spacer name="verticalSpacer">
       <property name="orientation">
        <enum>Qt::Vertical</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
         <width>20</width>
         <height>40</height>
        </size>
       </property>
      </spacer>
     </item>
     <item>
      <widget class="QPushButton" name="pushButton_OK">
       <property name="text">
        <string>OK</string>
       </property>
      </widget>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>
Gruß
Atalanttore
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

self.colors gehört klein geschrieben. Und was das dictionary angeht: da kommt es etwas auf die python version an. Seit 3.X (müsste genau nachschauen) ist das IMMER ein ordered dict. Hat also eine stabile, und von der einfügereihenfolge Abhängige Ordnung.

Davor ist es nicht garantiert gewesen, welche Ordnung man hat. Aber stabil im Sinne von zwei Iterationen liefern immer das gleiche Ergebnis, wenn es zwischendurch keine schreibende Veränderung gab, war es schon immer.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__deets__: Danke für die Antwort.
__blackjack__ hat geschrieben: Sonntag 11. November 2018, 15:44 Edit: Konstanten werden per Konvention immer noch KOMPLETT_GROSS geschrieben.
__deets__ hat geschrieben: Sonntag 25. November 2018, 13:20 self.colors gehört klein geschrieben.
Die Angabe der verschiedenen Farben ist nach meinem Verständnis eine Konstante, weil sie im Laufe des Programms nicht geändert wird. Deshalb habe ich den Bezeichner vor ein paar Wochen in Großbuchstaben geändert. Habe ich da etwas falsch verstanden?

__deets__ hat geschrieben: Sonntag 25. November 2018, 13:20 Und was das dictionary angeht: da kommt es etwas auf die python version an. Seit 3.X (müsste genau nachschauen) ist das IMMER ein ordered dict. Hat also eine stabile, und von der einfügereihenfolge Abhängige Ordnung.

Davor ist es nicht garantiert gewesen, welche Ordnung man hat. Aber stabil im Sinne von zwei Iterationen liefern immer das gleiche Ergebnis, wenn es zwischendurch keine schreibende Veränderung gab, war es schon immer.
Bei der Suche nach "ordered dict" habe ich dazu eine Bestätigung gefunden.

Gruß
Atalanttore
Sirius3
User
Beiträge: 17759
Registriert: Sonntag 21. Oktober 2012, 17:20

Konstanten werde nicht innerhalb einer Funktion definiert, und alles was als Parameter in einer Funktion verwendet wird, ist keine Konstante. Ich hatte COLORS als Klassenkonstante direkt im Klassenkörper definiert, aber sobald ich es an ConcentrationTest übergeben hatte, war das in diesem Kontext keine Konstante mehr, denn ich hätte dem Initialisierer auch eine Variable übergeben können.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@Sirius3: Danke für die Erklärung.
Antworten