QSlider bereitet kleine Probleme

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

Hallo

Ich probiere gerade die verschiedenen Widgets aus Qt 5 durch. Einiges hat schon wie erwartet funktioniert, aber der QSlider bereitet mir ein paar kleinere Probleme.


sliderwindow.ui:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>SliderWindow</class>
 <widget class="QMainWindow" name="SliderWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QWidget" name="">
    <property name="geometry">
     <rect>
      <x>40</x>
      <y>40</y>
      <width>721</width>
      <height>121</height>
     </rect>
    </property>
    <layout class="QGridLayout" name="gridLayout">
     <item row="0" column="0">
      <widget class="QLabel" name="label_slider">
       <property name="text">
        <string>Slider</string>
       </property>
      </widget>
     </item>
     <item row="0" column="1">
      <widget class="QSlider" name="horizontalSlider">
       <property name="maximum">
        <number>100</number>
       </property>
       <property name="sliderPosition">
        <number>50</number>
       </property>
       <property name="orientation">
        <enum>Qt::Horizontal</enum>
       </property>
      </widget>
     </item>
     <item row="1" column="1">
      <widget class="QLabel" name="label_value">
       <property name="text">
        <string/>
       </property>
      </widget>
     </item>
    </layout>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>28</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>


sliderwindow.py:

Code: Alles auswählen

import sys
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QDialog
from PySide2.QtCore import QFile


class SliderWindow(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.init_window()

    def init_window(self):
        self.file = QFile("sliderwindow.ui")
        self.file.open(QFile.ReadOnly)

        self.loader = QUiLoader()
        self.window = self.loader.load(self.file)
        self.window.show()

        self.window.horizontalSlider.sliderMoved.connect(self.write_slider_value)

    def write_slider_value(self):
        self.slider_value = self.window.horizontalSlider.value()
        self.window.label_value.setText(str(self.slider_value))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    sliderwindow = SliderWindow()

    sys.exit(app.exec_())
  1. Als `minimum` ist 0 und als `maximum` ist 100 für den QSlider im Qt Designer eingestellt. In der GUI reicht der QSlider allerdings nur von 1 bis 99. Warum ist das so?
  2. Fließkommazahlen für den Bereich des QSliders scheint der Qt Designer nicht zu mögen, weil er solche Werte in Integerzahlen rundet, sobald man sie eingegeben hat. Kann man für einen QSlider auch einen Bereich von 0.5 bis 9.5 mit der Schrittweite 0.1 angeben?
  3. Sobald der QSlider mit der Maus verschoben wird, würde ich gerne einen Tooltip mit dem jeweils aktuellen Wert neben dem Mauszeiger anzeigen lassen. Geht so etwas?
Gruß
Atalanttore
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Minimum sehe ich jetzt gerade nicht. Eigentlich sollten beide Werte erreichbar sein. Aber er ist definitiv immer Ganzzahlig. Da musst du halt umrechnen.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Mit `minimum` habe ich gemeint, dass dafür im Qt Designer (standardmäßig) der Wert 0 eingetragen ist.

Umrechnen in der Programmlogik ist kein Problem, aber eigentlich wollte ich als Tooltip eine Fließkommazahl anzeigen lassen, wenn der Slider mit der Maus bewegt wird (siehe Punkt 3).

Gruß
Atalanttore
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Tooltips gibt es eingebaut. Mit setToolTip kontrollierst du den Text. Das ist aber nur beim schweben lassen des Mauszeigers getriggert. Wenn du sowas machen willst wie einen Wert, der sich bestaendig aendert waehrend man mit der Maus den Slider bewegt, und dabei auch noch die Position dieser Anzeige am Mauszeiger ausrichten willst - dann wirst du das programmieren muessen. Ob das cool aussieht, und wie sich das auf verschiedenen Plattformen verhaelt musst du dann ausprobieren. Ich persoenlich wuerde ja eher ein Label oder gar LineEdit vorsehen, damit der geneigte Benutzer den Wert direkt eintragen kann.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Also der QSlider mag keine Fließkommazahlen (Punkt 2), ein Wert unmittelbar am Schieberegler geht auch nicht (Punkt 3) und das Problem mit den abweichenden Start-/Endwerten besteht auch (Punkt 1).

Kann man im QSlider ein mit Werten beschriftetes Raster anzeigen lassen?

Gruß
Atalanttore
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nicht das ich wüsste. In der Doku hast du bestimmt schon geschaut & nix gefunden, oder?

Mit ein bisschen programmieren kannst du dir aber problemlos ein Widget aus einem Qslider und einem Raster-Widget das du selbst schreibst bauen. Kein Toolkit dieser Welt kann jeden potentiellen Fall aus dem stehgreif abdecken. Seine Qualität besteht darin, Standardfälle einfach und fortgeschrittenes möglich zu machen.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Meine bevorzugte Suchmaschine des geringsten Misstrauens findet mehrere Dokus zu QSlider. Welche Doku meinst du genau?

Ich habe mir noch nie ein Widget selber gebaut. Spontan würde ich für meinen Wunschschieberegler eine eigene Klasse erstellen, die von Qslider erbt und mit einer eigenen Raster-Methode ergänzt wird. Würde das so funktionieren?

Gruß
Atalanttore
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na wenn in der offiziellen Doku nix steht, dann kann der QSlider das eher nicht. Und das ist auch nicht so wahnsinnig verwunderlich. Denn vielleicht willst du 5 Raster, oder 7? Große Ticks, Kleine, in drei Stufen gemischt?Mit römischen Zahlen, 33.5 Grad geneigten Labels? Das wurde schnell ausarten, da alle denkbaren Dinge anzubieten.

Darum selbst ein Raster-Widget schreiben. Ich würde aber genau NICHT ableiten. Sondern ein Widget bauen (ok, von QWidget musst du ableiten, weniger geht nicht) und das malt das raster. Nur das. Horizontal, vertikal, links oder rechtsbündig, mit einer gegebene Anzahl von schritten. Wie du es dir wünscht.

Und das dann mit in einem Container-Widget, zb einem Frame, zusammen mit dem Qslider kombinieren.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Ohne genaue Anleitung ist mir das selbst schreiben eines Raster-Widgets momentan noch zu hoch.

Kann man ein selbst geschriebenes Widget überhaupt mit dem Qt Designer in der GUI platzieren?

Gruß
Atalanttore
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Davon, das du es nicht probierst wird es nicht einfacher ;)

Und ja. Man kann die benutzen im Designer. Du kannst ein generisches Widget platzieren, aber dann abgeben, dass es eine bestimmte Klasse haben soll. Dann wird das erzeugt. Alternativ einfach benennen & zur Laufzeit austauschen. Und C++ Widgets kann man auch zu rechten Designer Widgets machen. Ob das mit Python geht weiß ich nicht aus dem stehgreif.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Ich weiß leider gar nicht wie und wo ich mit dem Probieren anfangen soll. :cry:

Gruß
Atalanttore
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Zu Punkt 1 habe ich gerade herausgefunden, dass der Wert erst auf 0 geht, wenn man den Slider vollständig nach links schiebt (Slider steht auf Wert 1) und anschließend mit gedrückter Maustaste langsam nach rechts fährt. Für den Wert 100 verhält es sich genau anders herum. Was geht da vor sich?

EDIT: Ich habe meinen Code gerade für PyQt5 umgeschrieben, aber das Problem verhält sich genauso wie mit PySide2.

Code: Alles auswählen

import sys
from PyQt5.uic import loadUi
from PyQt5.QtWidgets import QApplication, QMainWindow

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

    def init_window(self):
        self.ui = loadUi("sliderwindow.ui", self)
        self.show()

        self.horizontalSlider.sliderMoved.connect(self.write_slider_value)

    def write_slider_value(self):
        self.slider_value = self.horizontalSlider.value()
        self.label_value.setText(str(self.slider_value))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    sliderwindow = SliderWindow()

    sys.exit(app.exec_())
Gruß
Atalanttore
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Zur Erstellung von custom Widgets gibt’s viele Tutorials. Meistens zwar in C++, s er das ist in Qt halt so. Das ist auch nicht so schwer zu übersetzen.

Und warum der sich so verhält- ka. Mit Python hat das aber eher nichts zu tun. Frag/such in qt Foren.
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: self.slider_value gehört eher nicht als Attribut auf das Objekt, das ist eine ganz normale lokale Variable.

Die Aufteilung der Initialisierung in `__init__()` und `init_window()` macht keinen Sinn. Ebensowenig wie `self.ui` wenn das Objekt selbst ja schon diese `ui` ist. `show()` gehört nicht in die Initialisierung, sondern ausserhalb der Klasse.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__blackjack__:
  1. Ist die Variable `self.slider_value`, die innerhalb der Methode `write_slider_value()` definiert wird, nicht schon eine ganz normale lokale Variable?
  2. Was meinst du mit `self.ui` genau?
Alles andere habe ich verstanden (glaube ich zumindest :roll: ) und den Code entsprechend angepasst.

sliderwindow.py:

Code: Alles auswählen

import sys
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QMainWindow
from PySide2.QtCore import QFile


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

        self.file = QFile("sliderwindow.ui")
        self.file.open(QFile.ReadOnly)

        self.loader = QUiLoader()
        self.window = self.loader.load(self.file)

        self.window.horizontalSlider.sliderMoved.connect(self.write_slider_value)

    def write_slider_value(self):
        self.slider_value = sliderwindow.window.horizontalSlider.value()
        self.window.label_value.setText(str(self.slider_value))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    sliderwindow = SliderWindow()

    sliderwindow.window.show()

    sys.exit(app.exec_())
Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: 1. nein `self.slider_value` ist im Grunde gar keine Variable sondern ein Attribut auf dem Objekt. `slider_value` wäre eine Variable. Und wenn an diesen Namen in der Methode eine Wert gebunden wird, dann ist der Name lokal in der Methode.

2. Mit `self.ui` meine ich `self.ui`. Das benutzt Du nirgends und deshalb macht es auch keinen Sinn das zu definieren. Es zu benutzen würde auch keinen Sinn machen, denn alles was darüber erreichbar ist, ist ja *auch* über das Objekt selbst erreichbar, denn das wurde ja an `loadUI()` als Argument übergeben.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__blackjack__:
  1. Okay. Mit dem Schlüsselwort `self`, also `self.slider_value`, wird die Variable zum Attribut, weil es im gesamten Objekt gültig ist (nennt man das "gültig"?) und in dem Fall ist das nicht nötig. Ohne `self`, also `slider_value`, ist die Variable nur in der Methode `write_slider_value()` gültig.
  2. Sorry, aber ich und die Suchfunktion meiner IDE finden im Code keinen Bezeichner namens `self.ui`.
    BTW: Den Code in der Methode `__init__()` habe ich ursprünglich aus einem Beispiel und anschließend selbst zu einer Klasse umgebaut.
Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Der letzte Beitrag von Dir vor meiner Antwort mit dem `self.ui` war der hier: viewtopic.php?f=24&t=44062#p334474

Und da sehe ich auch ohne Suchfunktion ein ``self.ui = …``. Auf das bezog ich mich.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__blackjack__: Den Test mit PyQt5 habe ich wegen dem Problem mit dem Werten des Sliders kurz durchgeführt. Danach habe ich den Code wieder verworfen, wobei mir sowieso nicht klar ist, ob man lieber PySide2 oder PyQt5 verwenden sollte.

Für die Frage PySide2 oder PyQt5 habe ich ein eigenes Thema erstellt.

Gruß
Atalanttore
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

In meinem Beispiel bin ich jetzt von PySide2 auf PyQt5 umgestiegen und habe es um einen Dialog erweitert, den man über das Menü aufrufen kann. Als Vorlage habe ich das Beispiel von hier genommen.


mainwindow.ui

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>SliderWindow</class>
 <widget class="QMainWindow" name="SliderWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QWidget" name="layoutWidget">
    <property name="geometry">
     <rect>
      <x>40</x>
      <y>40</y>
      <width>721</width>
      <height>51</height>
     </rect>
    </property>
    <layout class="QGridLayout" name="gridLayout">
     <item row="0" column="0">
      <widget class="QLabel" name="label_value">
       <property name="minimumSize">
        <size>
         <width>50</width>
         <height>20</height>
        </size>
       </property>
       <property name="text">
        <string/>
       </property>
      </widget>
     </item>
     <item row="0" column="1">
      <widget class="QSlider" name="horizontalSlider">
       <property name="minimum">
        <number>0</number>
       </property>
       <property name="maximum">
        <number>100</number>
       </property>
       <property name="sliderPosition">
        <number>50</number>
       </property>
       <property name="orientation">
        <enum>Qt::Horizontal</enum>
       </property>
      </widget>
     </item>
    </layout>
   </widget>
   <widget class="QPushButton" name="pushButton_exit">
    <property name="geometry">
     <rect>
      <x>610</x>
      <y>430</y>
      <width>117</width>
      <height>32</height>
     </rect>
    </property>
    <property name="text">
     <string>Beenden</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>28</height>
    </rect>
   </property>
   <widget class="QMenu" name="menu_file">
    <property name="title">
     <string>Da&amp;tei</string>
    </property>
    <addaction name="action_settings"/>
   </widget>
   <addaction name="menu_file"/>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <action name="action_settings">
   <property name="text">
    <string>&amp;Einstellungen</string>
   </property>
  </action>
 </widget>
 <resources/>
 <connections/>
</ui>

settingswindow.ui

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>SettingsWindow</class>
 <widget class="QDialog" name="SettingsWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Einstellungen</string>
  </property>
  <widget class="QDialogButtonBox" name="buttonBox">
   <property name="geometry">
    <rect>
     <x>30</x>
     <y>240</y>
     <width>341</width>
     <height>32</height>
    </rect>
   </property>
   <property name="orientation">
    <enum>Qt::Horizontal</enum>
   </property>
   <property name="standardButtons">
    <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections>
  <connection>
   <sender>buttonBox</sender>
   <signal>accepted()</signal>
   <receiver>SettingsWindow</receiver>
   <slot>accept()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>248</x>
     <y>254</y>
    </hint>
    <hint type="destinationlabel">
     <x>157</x>
     <y>274</y>
    </hint>
   </hints>
  </connection>
  <connection>
   <sender>buttonBox</sender>
   <signal>rejected()</signal>
   <receiver>SettingsWindow</receiver>
   <slot>reject()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>316</x>
     <y>260</y>
    </hint>
    <hint type="destinationlabel">
     <x>286</x>
     <y>274</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>

mainwindow.py

Code: Alles auswählen

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


class MainWindow(QMainWindow):

    switch_window = QtCore.pyqtSignal() # 1

    def __init__(self, parent=None):
        super().__init__(parent)
        loadUi("mainwindow.ui", self)

        self.horizontalSlider.sliderMoved.connect(self.write_slider_value)
        self.action_settings.triggered.connect(self.switch) # 2
        #self.pushButton_exit.clicked.connect(self.quit, parent=self) # funktioniert nicht

    def write_slider_value(self):
        slider_value = self.horizontalSlider.value()
        self.label_value.setText(str(slider_value))

    def switch(self):
        self.switch_window.emit() # 3


class SettingsWindow(QDialog):

    def __init__(self, parent=None):
        super().__init__(parent)
        loadUi("settingswindow.ui", self)


class Controller:

    def __init__(self):
        pass

    def show_main_window(self):
        self.main_window = MainWindow()
        self.main_window.switch_window.connect(self.show_settings_window) # 4
        self.main_window.show()

    def show_settings_window(self):
        self.settings_window = SettingsWindow()
        self.settings_window.show()



if __name__ == "__main__":
    app = QApplication(sys.argv)
    controller = Controller()
    controller.show_main_window()
    sys.exit(app.exec_())
Irgendwie öffnet sich durch die Methoden `QtCore.pyqtSignal()` und `emit()` tatsächlich der gewünschte Dialog `SettingsWindow`. Die Doku zu Signals and Slots habe ich zwar gelesen, aber trotzdem weiß ich nicht was bei den nummerierten Stellen 1 bis 4 genau vor sich geht.

Mein nächster Versuch den Button zum Beenden mit einer Methode zum Beenden zu verknüpfen ist mit folgender Fehlermeldung erneut gescheitert.

Code: Alles auswählen

 File "/home/ata/source/slider/mainwindow.py", line 53, in <module>
    controller.show_main_window()
  File "/home/ata/source/slider/mainwindow.py", line 40, in show_main_window
    self.main_window = MainWindow()
  File "/home/ata/source/slider/mainwindow.py", line 17, in __init__
    self.pushButton_exit.clicked.connect(self.quit, parent=self)
AttributeError: 'MainWindow' object has no attribute 'quit'
Gruß
Atalanttore
Antworten