Re: Button auslesen
Verfasst: Mittwoch 8. September 2021, 11:48
Schönen Dank für die Hinweise, werde ich mal ausprobieren!
Seit 2002 Diskussionen rund um die Programmiersprache Python
https://www.python-forum.de/
bitte versteh' mich nicht falsch, ich möchte Dir wirklich nicht auf die Füße treten. Deine Lösung gefällt mir (halbwegs) und ich weiß, daß grob geschätzt 99,9% aller Tkinter-Beispiele prozedural implementiert sind. Aber ich finde das grausam... wie gesagt: sei mir nicht böse, bitte. Bei GUIs halte ich Objektorientierung für dringendst angeraten. Tkinter war dabei über lange Jahre hinweg lang ein Mistfink, weil die Komponenten nicht von builtins.object geerbt haben und deswegen so etwas häßliches gebraucht haben wie
Code: Alles auswählen
class Dings(tkinter.Tk):
def __init__(self, *args, **kwargs):
tkinter.Tk.__init__(self, *args, **kwargs)
[...]
Code: Alles auswählen
#!/usr/bin/env python
import tkinter as T
class Action:
def __init__(self, topwin, x, y):
self.topwin = topwin
self.x = x
self.y = y
def __call__(self, *args, **kwargs):
self.topwin.xy[self.x][self.y].config(foreground='white', background='green')
self.topwin.update()
class MainWin(T.Tk):
def __init__(self, x=1, y=1, *args, **kwargs):
self.x = x
self.y = y
super().__init__(*args, **kwargs)
self.xy = []
for x in range(self.x):
frame = T.Frame(self)
frame.pack(side=T.TOP)
col = []
for y in range(self.y):
btn = T.Button(frame, text='•', command=Action(self, x, y))
btn.pack(side=T.LEFT)
col.append(btn)
self.xy.append(col)
if __name__ == '__main__':
MainWin(25, 5).mainloop()
Code: Alles auswählen
import tkinter as tk
from functools import partial
class MainWin(tk.Tk):
def __init__(self, row_count=1, column_count=1, *args, **kwargs):
super().__init__(*args, **kwargs)
self.table = []
for row_index in range(row_count):
column = []
for column_index in range(column_count):
button = tk.Button(self, text='•', command=partial(self.update_button_color, row_index, column_index))
button.grid(row=row_index, column=column_index)
column.append(button)
self.table.append(column)
def update_button_color(self, row_index, column_index):
self.table[row_index][column_index].config(foreground='white', background='green')
def main():
mainwin = MainWin(25, 5)
mainwin.mainloop()
if __name__ == '__main__':
main()
Code: Alles auswählen
import tkinter as tk
class CustomButton(tk.Button):
def __init__(self, row, column, *args, **kwargs):
super().__init__(*args, **kwargs)
self["command"] = self.toggle_face
self["text"]= "•"
self.grid(row=row, column=column)
def toggle_face(self):
if self["bg"] == "green":
self["bg"] = "SystemButtonFace"
else:
self["bg"] = "green"
class MainWindow(tk.Tk):
def __init__(self, rows, columns, *args, **kwargs):
super().__init__(*args, **kwargs)
for row in range(rows):
for column in range(columns):
CustomButton(row, column)
app = MainWindow(10, 10)
app.mainloop()
Die Entwickler sehen das anders, fürchte ich: https://docs.python.org/3/library/tkinter.html__blackjack__ hat geschrieben: Freitag 10. September 2021, 10:11 Bezüglich `super()` sollte man vielleicht auch noch sagen, dass das mit `tkinter` nicht richtig funktioniert.
Unter Python2 hat super() mit Tkinter nicht funktioniert. Wenn ich mich recht entsinne (ich bin zu faul, das jetzt nochmal zu recherchieren, zumal Python2 ja ohnehin EOL ist), dann war das deswegen, weil Frederik Lundhs Tkinter-Klassen nicht von object geerbt haben, was aber die Voraussetzung für die Verwendung von super() war. Heute sind diese expliziten Aufrufe nicht mehr notwendig, denn in Python3 erben alle Klassen ohnehin implizit von object und erfüllen damit die Voraussetzungen, die super() braucht. Insofern sind also sowohl der alte Code als auch die neue Dokumentation absolut korrekt.Sirius3 hat geschrieben: Freitag 10. September 2021, 11:44 Die Dokumentation ist ganz sicher falsch, denn Basisklassen werden über BasisKlasse.__init__(...) aufgerufen: https://github.com/python/cpython/blob/ ... _.py#L2649
Da ich das mit dem OOP-Tkinter schon seit ziemlich langer Zeit genau so mache, wie es auch die Dokumentation von Python3 zeigt, und ich damit noch nie ein Problem gehabt habe, gehe ich weiterhin davon aus, daß die Dokumentation korrekt ist und demzufolge auch meine Ausführungen dazu korrekt sind. Allerdings kannst Du mich gerne überzeugen, indem Du ein lauffähiges Codebeispiel postest, welches zeigt, daß "super().<methode>(*args, **kwargs)" tatsächlich etwas anderes tut als "Basisklasse.<methode>(self, *args, **kwargs)". Viel Erfolg!
Code: Alles auswählen
#!/usr/bin/env python3
from tkinter import Tk, Button
class SomeMixin:
def __init__(self, *args, **kwargs):
print("SomeMixin init")
super().__init__(*args, **kwargs)
class SpecializedButton(Button, SomeMixin):
def __init__(self, *args, **kwargs):
print("SpecialButton init")
super().__init__(*args, **kwargs)
def main():
root = Tk()
button = SpecializedButton(root)
button.pack()
root.mainloop()
if __name__ == "__main__":
main()
Das hat aber nichts mit Tkinter zu tun, sondern mit Mehrfachvererbung und den subtilen Spezialitäten, wie super() die MRO abarbeitet: die wird nämlich von links nach rechts durchsucht, und wenn ein Attribut des gewünschten Namens vorhanden ist, eben dieses benutzt. Dieser Code hier hat dasselbe "Problem", obwohl Tkinter offensichtlich gar nicht involviert ist UND alle __init__()-Methoden super().__init__() aufrufen:__blackjack__ hat geschrieben: Samstag 11. September 2021, 18:07 Die `SomeMixin.__init__()` wird nicht aufgerufen. Und zwar weil `Button` kein `super()` benutzt. Damit `super()` so funktioniert wie es soll, muss wie schon mal gesagt, jede betroffene Methode `super()` verwenden. Sonst kann `super()` nicht das machen wofür es gedacht ist.
Code: Alles auswählen
#!/usr/bin/env python
class P:
def __init__(self):
print(' P.__init__()')
super().__init__()
class F(P):
def __init__(self):
print(' F.__init__()')
super().__init__()
class A(F):
def __init__(self):
print(' A.__init__()')
super().__init__()
class B(F):
def __init__(self):
print(' B.__init__()')
super().__init__()
class C(A, B):
def __init__(self):
print('C.__init__()')
super().__init__()
if __name__ == '__main__':
c = C()
Code: Alles auswählen
C.__init__()
A.__init__()
B.__init__()
F.__init__()
P.__init__()
Code: Alles auswählen
class SpecializedButton(SomeMixin, Button):
Code: Alles auswählen
class P:
def __init__(self):
print(' P.__init__()')
super().__init__()
class F(P):
def __init__(self):
print(' F.__init__()')
super().__init__()
class Q:
def __init__(self):
print(' Q.__init__()')
super().__init__()
class A(Q):
def __init__(self):
print(' A.__init__()')
super().__init__()
class B(F):
def __init__(self):
print(' B.__init__()')
super().__init__()
class C(A, B):
def __init__(self):
print('C.__init__()')
super().__init__()
if __name__ == '__main__':
c = C()
Code: Alles auswählen
C.__init__()
A.__init__()
Q.__init__()
B.__init__()
F.__init__()
P.__init__()
Code: Alles auswählen
class P:
def __init__(self):
print(' P.__init__()')
super().__init__()
class F(P):
def __init__(self):
print(' F.__init__()')
super().__init__()
class Q:
def __init__(self):
print(' Q.__init__()')
#super().__init__()
class A(Q):
def __init__(self):
print(' A.__init__()')
super().__init__()
class B(F):
def __init__(self):
print(' B.__init__()')
super().__init__()
class C(A, B):
def __init__(self):
print('C.__init__()')
super().__init__()
if __name__ == '__main__':
c = C()
Code: Alles auswählen
C.__init__()
A.__init__()
Q.__init__()
Naja, das ist aus meiner Sicht leider nicht ganz richtig. Tkinter ruft die Methoden (bzw. Konstruktoren) der Elternklassen (je nach Bedarf) nicht über super(), sondern explizit auf, was im Zweifelsfall ohnehin immer die sicherste und zuverlässigste Möglichkeit ist. Sie werden also aufgerufen, und das funktioniert wunderbar. Daran ändert sich auch nichts, wenn von Tkinters Klassen erbende Kindklassen für den Aufruf von Elternmethoden super() benutzen -- darauf muß man nur achten, wenn man selbst Mehrfachvererbung benutzt, woran __blackjack__ und freundlicherweise noch einmal erinnert hat. Am Ende hat das aber, wie gesagt, nichts mit Tkinter zu tun, sondern mit Mehrfachvererbung, der MRO und der Spezialitäten von super() -- das sich trotz allem immer noch wundernbar mit Tkinter nutzen läßt.kbr hat geschrieben: Samstag 11. September 2021, 20:55 Nun deaktivieren wir den Aufruf von super() in Q, so wie es analog in vielen tkinter-Klassen erfolgt:
Es funktioniert nicht wunderbar, da die Methoden der tkinter-Elternklassen ihrerseits nicht object.__init__ aufrufen; genau das hatte ich mit dem Beispiel gezeigt. Dieser Aufruf ist aber erforderlich, damit super im Falle von Mehrfachvererbung in Verbindung mit tkinter-Klassen korrekt funktionieren kann. Allein dadurch, dass der Python 2 old-style-classes tkinter-code mit Python 3 auf einmal implizit von object erbt, ändert sich gar nichts. tkinter ist schlicht und einfach nicht für Mehrfachvererbung in Verbindung mit der MRO-Serialisierung von super entworfen. Und der explizite Aufruf von Methoden der Elternklassen ist nicht die sicherste und zuverlässigste Möglichkeit, sondern lediglich die Explizite.LukeNukem hat geschrieben: Montag 13. September 2021, 17:51 Naja, das ist aus meiner Sicht leider nicht ganz richtig. Tkinter ruft die Methoden (bzw. Konstruktoren) der Elternklassen (je nach Bedarf) nicht über super(), sondern explizit auf, was im Zweifelsfall ohnehin immer die sicherste und zuverlässigste Möglichkeit ist. Sie werden also aufgerufen, und das funktioniert wunderbar.
Ich nehme Deinen Einwand zur Kenntnis, aber bitte sei mir nicht böse, wenn ich jetzt einmal einen ehemaligen Bundesminister des Auswärtigen zitieren muß: ich bin nicht überzeugt. Ich nutze Tkinter seit langer Zeit nur noch objektorientiert und habe, seit ich Python3 nutze, niemals Probleme mit super() gehabt. Mir geht allerdings die Hybris ab, das als Beleg werten oder gar anführen zu wollen, das wäre natürlich ein argumentativer Fehlschluß und dieses hohen Hauses unwürdig. Daß die offizielle Dokumentation zu Tkinter diese Technik ebenfalls empfiehlt, wäre insofern allenfalls ein argumentum ad populum.kbr hat geschrieben: Montag 13. September 2021, 20:45 Es funktioniert nicht wunderbar, da die Methoden der tkinter-Elternklassen ihrerseits nicht object.__init__ aufrufen; genau das hatte ich mit dem Beispiel gezeigt. Dieser Aufruf ist aber erforderlich, damit super im Falle von Mehrfachvererbung in Verbindung mit tkinter-Klassen korrekt funktionieren kann. Allein dadurch, dass der Python 2 old-style-classes tkinter-code mit Python 3 auf einmal implizit von object erbt, ändert sich gar nichts.
Das sagst Du, und andere Menschen in diesem Forum ebenfalls. Mir dagegen fällt es aufgrund meiner gegenteiligen Erfahrungen und der Tatsache, daß es in der Python-Dokumentation genau so gezeigt wird, schwer, das zu glauben (ganz abgesehen davon, daß das nicht mein Punkt war, aber sei's drum). Da wir hier aber in einem Forum zu einer ausgesprochen zuverlässigen Programmiersprache sind, die sich als enorm stabil erwiesen hat und in der Regel absolut reproduzierbare Ergebnisse hervorbringt, möchte ich auch Dich sehr herzlich darum bitten, mir Deine Aussagen als Beispiel in lauffähigem Code zu demonstrieren. Und zwar bitte nicht wie Dein Vorgänger unter Mißachtung der MRO, sondern bitte an Code, der Deine Ausführungen ganz konkret an und mit Tkinter zeigt. Wenn Du das kannst, dann mache ich mir die Arbeit -- auf Wunsch natürlich mit ausdrücklicher Bezugnahme auf Dich -- einen Bugreport zu verfassen, damit die Dokumentation korrigiert wird. Lieben Dank für Deine Bemühungen!kbr hat geschrieben: Montag 13. September 2021, 20:45 tkinter ist schlicht und einfach nicht für Mehrfachvererbung in Verbindung mit der MRO-Serialisierung von super entworfen.
Ja, genau -- und dieses Explizite macht es aus meiner Perspektive zur sichersten und zuverlässigsten Möglichkeit. super() ist etwas, das primär den "Faulpelzen" mit ihrem Streben nach wartbarem und wiederverwendbarem Code hilft. Befremdliche Pedanten wie ich, die PEP20 mögen ("explicit is better than implicit"), mögen die explizite Variante prinzipiell lieber, denn wenn man es richtig macht, funktioniert sie immer und gibt uns als Entwicklern die präziseste mögliche Kontrolle darüber, was, wann, wo und wie aufgerufen wird!kbr hat geschrieben: Montag 13. September 2021, 20:45 Und der explizite Aufruf von Methoden der Elternklassen ist nicht die sicherste und zuverlässigste Möglichkeit, sondern lediglich die Explizite.