ttk buttons/widget states

Fragen zu Tkinter.
Antworten
blackdiamont
User
Beiträge: 6
Registriert: Donnerstag 12. August 2021, 16:25

Moinsen,

ich hoffe ihr könnt mir Helfen und bin auch im richtigen Forum ;)
Bisher progge ich in PHP und C (Arduino), daher bin ich noch nicht so bewandert mit Python.

Ich habe mir eine App geforked welche auf picamera und ttk basiert.
Hier bin ich dabei die offenen ToDo's etc zu verwirklichen, was auch Hindernisse mit sich bringt wo ich erst einmal Doku's etc durchforsten muss um zu verstehen wie es funktioniert usw.

Aktuell hänge ich am "simplen" Button "disablen", was laut Doku mit state="disabled" funktioniert (Testcode tut).
Die Buttons.py bereitet mir etwas Kopfschmerzen ... es geht um den MultiTouchButton.

Code: Alles auswählen

LangLabel(f10,text='Exposure Mode:',
			language=self.language).grid(row=4,column=0,sticky='E',pady=(15,0))
		self.exposureMode = MultiTouchButton(f10,text=['off','auto','night','nightpreview','backlight','spotlight','sports','snow','beach','verylong','fixedfps','antishake','fireworks'],
			value=Globals.exposureMode,language=self.language,
			background=Globals.defaultBackgroundColor,
			callback=self.ExposureModeChanged,width=200)
		self.exposureMode.grid(row=4,column=1,padx=(15,0),pady=(15,0))
		self.ExposureModeChanged(Globals.exposureMode)
Der Button funktioniert an sich, allerdings soll dieser deaktiviert werden sobald ISO nicht mehr auf auto steht.
Mein simpler Gedanke bzw eher versuch war ein state="disabled" hinzuzufügen, was ignoriert wird ...

Bin mir auch nicht sicher ob dieser Code nur einmal ausgeführt wird beim Start oder dauerhaft, was nMm der Fall sein sollte.

Habt ihr eine Idee? :)

Grüssle
BLACK
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Da ist so einiges schlecht an dem Code, den Du Dir da ausgesucht hast. Variablen und Methoden schreibt man komplett klein, man rückt mit 4 Leerzeichen pro Ebene ein und nicht mit Tabs. Man bricht eine Zeile immer nach einem : um, man benutzt keine nackten Excepts. Man verwendet keine *-Importe.
Deshalb sollte MultiTouchButton eigentlich ungefähr so aussehen:

Code: Alles auswählen

class MultiTouchButton(ttk.Frame):
    def __init__(self, parent, *args, **kargs):
        ttk.Frame.__init__(self, parent)
        self.config(style='F.TFrame')
        self._parent = parent
        self._callback = kargs.get('callback')
        self.lang = kargs.get('language')
        self._text = kargs['text']
        if not isinstance(self._text, list) or not isinstance(self._text[0], str):
            raise ValueError("'text' must be a list of strings")
        self._width = int(kargs.get('width', 50))
        self._height = int(kargs.get('height', Globals.defaultButtonHeight))
        self.value = kargs['value']
        self._canvas = Canvas(self,
            width=self._width,
            height=self._height,
            background=Globals.buttonbackcolor)
        self._canvas.grid(row=0, column=0)
        self._canvas.bind('<Button-1>', self.pressed)
        self._canvas.bind('<ButtonRelease-1>', self.released)
        self._text_area = self._canvas.create_text(
            (int(self._width / 2), int(self._height / 2)),
            font=Globals.defaultFont,
            fill=Globals.buttontextcolor,
            text='')
        self.draw_text()

    def pressed(self, event):
        self._canvas.config(background=Globals.buttonpressedcolor)
        self._canvas.itemconfig(self._text_area, fill=Globals.buttonpressedtextcolor)

    def released(self, event):
        self._value += 1
        if self._value >= len(self._text):
            self._value = 0
        self._canvas.config(background=Globals.buttonbackcolor)
        self.draw_text()
        if self._callback:
            self._callback(self._value)

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, val):
        self._value = min(len(self._text) - 1, max(0, int(val)))
        self.draw_text()

    def draw_text(self):
        txt = self._text[self._value]
        if self.lang:
            txt = self.lang.get_text(txt)
        self._canvas.itemconfig(
            self._text_area,
            fill=Globals.buttontextcolor,
            text=txt)

    def update_language(self):
        self.draw_text()
Und das ist kein Button sondern ein Canvas und daher funktioniert auch state nicht, wenn man es nicht selbst programmiert.
Warum das kein Button ist, ist das Geheimnis des ursprünglichen Programmierers.
blackdiamont
User
Beiträge: 6
Registriert: Donnerstag 12. August 2021, 16:25

Danke für dein schnelle Antwort.
Also erstes ToDo wäre quasi den Code zu überarbeiten ... uff :/

Die Buttons kann ich ja nach deinem Beispiel einmal überarbeiten und schaue mir die Doku zu den Canvas einmal an.

Muss sagen das mit dem Einrücken via Tabs finde ich angenehmer als mit Leerzeichen, aber ich denke mal das ist so ne "Glaubenssache" :D
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@blackdiamont: Eine sehr starke Glaubenssache, beziehungsweise teilweise halt auch nicht, denn Einrückung ist Teil der Syntax, also glaubt der Compiler auch daran. Und wenn das lesbar und unmissverständlich für Menschen sein soll, dann darf man Tabs und Leerzeichen nicht mischen. *Alle* nehmen vier Leerzeichen und keine Tabs. Das ist ähnlich wie die Frage auf welcher Seite der Strasse man fährt. Eigentlich ist es ja egal ob man das wie bei uns oder wie in England macht. Solange das alle gleich machen. Mit Tabs bist Du bei Python der Geisterfahrer. Und wenn es einen Unfall gibt, ist das Deine Schuld. 🙂

Was ist an Tabs angenehmer? Jeder brauchbare Editor kann so eingestellt werden, dass bei der Tab-Taste vier Leerzeichen eingefügt werden, beziehungsweise so viele Leerzeichen wie nötig sind um an die entsprechende Stelle zu kommen. Und das selbe wenn man die Rücktaste drückt, dass dann entsprechend viele Leerzeichen entfernt werden. Also genau wie bei Tabs.

Welche Frage sich bei Leerzeichen *nicht* mehr stellt, ist wo denn eigentlich die Tabstops liegen. Alle 8 Zeichen? Alle 4 Zeichen? Was anderes? Oder gar nicht regelmässig? Und bekommt man das überall gleich hin? Wo stellst Du das im Browser ein? Wo im E-Mail-Client? Wo im Terminal? Eine halbwegs ”sichere” Einstellung wäre der Tabstop alle 8 Zeichen. Das ist aber für *eine* Einrückungsebene ziemlich (zu) gross. Aber Leerzeichen und Tabs mischen führt sehr leicht zu Problemen weil man leicht Code schreiben kann der was anderes bedeutet als der visuelle Eindruck vermittelt.

Konvention ist vier Leerzeichen pro Ebene, und diese Konventionen sind in aller Regel ziemlich starke Überzeugungen. Der wohl beliebteste Autoformatter für Python-Quelltext hat genau zwei mögliche Stellschrauben: Maximale Zeilenlänge, weil der Style Guide da zwischen 79 und 120 Zeichen pro Zeile vorsieht, und ob alle Zeichenkettenliterale von ' auf " als Begrenzer umgestellt werden sollen oder nicht. Alles andere ist nicht mehr die Entscheidung des Autors vom Quelltext der formatiert wird. Da wird nach Style Guide for Python Code formatiert, und wo dort Freiheiten gegeben sind, ist ein wichtiges Kriterium von black (so heisst der Formatter) das möglichst sinnvolle Diffs bei künftigen Änderungen entstehen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
blackdiamont
User
Beiträge: 6
Registriert: Donnerstag 12. August 2021, 16:25

Mischen ist klar, auf irgendwas sollte man sich schon einigen. Das sehe ich auch bei PHP so, entweder Prozedural oder OOP (hier bin ich auch der Prozedurale Fan). Einrücken joa mit Tabs gewohnt eigentlich, gerade im Terminal sehr angenehm daher bin ich der Tab Fan :)

Muss auch zugeben, bis auf die Arduino IDE verwende ich für alles andere keine IDE sondern gedit, nano oder wenn ich gerade kein Linux System habe den Notepad++.
Vielleicht gibt es ja eine IDE die ich für alles nutzen kann, auch unterwegs ;)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@blackdiamont: __blackjack__ hat nirgends was von IDE geschrieben. gedit, nano, Notepad++, alle kann man so einstellen, dass die Einrückung mit 4 Leerzeichen beim Drücken der Tab-Taste erfolgt, wenn es nicht gar schon die Standardeinstellung für Python-Quelltext ist.

Und wo rückst Du im Terminal mit Tabs ein?
blackdiamont
User
Beiträge: 6
Registriert: Donnerstag 12. August 2021, 16:25

Das mit IDE war ein Gedanke von mir.

Mit Terminal meine ich nano, vim etc
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Auch vim rückt beim Drücken von Tab mit Leerzeichen ein.

Code: Alles auswählen

:set expandtab tabstop=4
Bei Python-Quelltext ist das bei mir sogar voreingestellt.
blackdiamont
User
Beiträge: 6
Registriert: Donnerstag 12. August 2021, 16:25

Habs bei gedit eingestellt und auch leerzeichen statt Tab.
Danke schon mal soweit :)
Sirius3 hat geschrieben: Donnerstag 12. August 2021, 20:08 Man verwendet keine *-Importe.
Hast du mir ein Beispiel wie es korrekt sein sollte? Von der Doku selbst wird das so vorgegeben :shock:

Wie ist das mit dem "Mainloop" diesen ruft er mit

Code: Alles auswählen

microVIEWApp.mainloop()
auf, aber sehe nicht von welcher Zeile er Anfängt und Endet ...
Kenne das bisher nur mit while, for oder until Schleifen.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Ja, auch Dokumentationen sind nicht perfekt. Und die Dokumentation beschreibt auch gleich die Probleme, die man damit hat, nämlich dass das nicht 100% ersetzbar ist und man zum Schluß nicht weiß, welches Label denn benutzt wird, bzw. es davon abhängt, in welcher Reihenfolge was importiert wird.
Will man es wirklich austauschbar machen, dann sollte der Import so aussehen:

Code: Alles auswählen

from tkinter.ttk import Label, Button
bzw.

Code: Alles auswählen

from tkinter import Label, Button
Dann ist klar, welches Label benutzt wird. Noch besser ist es aber

Code: Alles auswählen

from tkinter import ttk
import tkinter as tk
zu benutzen, dann kann man nämlich wählen, ob man ttk.Label oder tk.Label schreibt.
`mainloop` ist eine ganz normale Methode, die intern eine Endlosschleife enthält, so dass sie erst zurückkehrt, wenn auch das Fenster wieder geschlossen wird. Dort wird eben auf Maus-, Tastatur- und andere Ereignisse gewartet, und diese dann verarbeitet.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@blackdiamont: Prozedural oder OOP ist keine sinnvolle Unterscheidung in PHP oder Python weil auch OOP hier letztlich prozeduralen Code enthält. Da muss man schon eine Sprache wie Io nehmen, oder Smalltalk und sich dort mit prozeduralem Code zurückhalten.

In Python kann man sich bei Tk (und den meisten anderen GUI-Rahmenwerken) eigentlich gar nicht gegen OOP entscheiden. Andererseits sollte man keine Klassen für Dinge schreiben die mit Funktionen ausgedrückt werden können, denn nicht die ``class``-Anweisung macht eine Klasse zu einer Klasse, sondern der Inhalt einer Klasse bestimmt, ob das tatsächlich eine Klasse ist, oder nur umständlich verkleidete Funktionen. So wie Du das formulierst, klingt das als müsste man sich zwischen Funktionen und Klassen entscheiden, und müsste bei OOP dann alles so zwanghaft in Klassen stopfen wie bei Java.

Anderseits kann man in Python gar nicht ohne Objekte, denn *alles* was man an einen Namen binden kann, ist in Python ein Objekt. Auch Module, Funktionen, und Klassen beispielsweise.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
blackdiamont
User
Beiträge: 6
Registriert: Donnerstag 12. August 2021, 16:25

@Sirius3
Danke dir für's Aufklären :D :)
So langsam verstehe ich, wie es funktioniert. Ich werde mir lokal mal ein paar Beispiele anschauen und versuchen selbst was zu Basteln.
Komme wohl nicht darum den "schlechten" Code, komplett zu Verbessern.

@__blackjack__
Guter Punkt. Ich sehe teilweise PHP-Code der 90% Prozedural und 10% OOP hat (Schulaufgaben z.b.), wo ich den Sinn dahinter einfach nicht verstehe.
Mit OOP tue ich mich ehrlich gesagt noch etwas Schwer, daher bin ich bei PHP auch noch dabei (solange ich es nur für mich selbst Progge). Die Vorteile sind mir Bekannt und es ist auch interessant soweit :)
__blackjack__ hat geschrieben: Freitag 13. August 2021, 22:24 denn nicht die ``class``-Anweisung macht eine Klasse zu einer Klasse, sondern der Inhalt einer Klasse bestimmt, ob das tatsächlich eine Klasse ist, oder nur umständlich verkleidete Funktionen.
Gut zu wissen, darauf habe ich noch nie Geachtet.

Jedenfalls Danke euch beiden vielmals für eure Zeit etc :)
Habe jetzt einiges auf der ToDo :D
Antworten