Anzeige flackert; wird nicht aufgebaut

Fragen zu Tkinter.
Antworten
lbuega
User
Beiträge: 75
Registriert: Dienstag 15. April 2003, 08:51
Wohnort: Weissach

Donnerstag 12. Juni 2003, 09:42

Hallo,
um das Aussehen der verwendeten Buttons in meinem Programm zu vereinheitlichen, hab ich gedacht es ist das Beste wenn ich hierfür eine eigene Klasse bilde, die von der Pythonklasse "Button" abgeleitet ist:

Code: Alles auswählen

from Tkinter import *

class OwnButton(Button):
    """_('Eigene Klasse >>OwnButton<< abgeleitet von der Python-Klasse
    >>Button<<; definiert das Aussehen der im Programm verwendeten Buttons')
    """
    def __init__(self, master, text, command):
        Button.__init__(self,master)
        self.button = Button(self, text=text, bd=3, bg=col_um_bg, fg=col_um_fg, font=("arial", 10, "bold"), command=command)
        self.button.pack()

exit_button = OwnButton(frame2, _('Abbrechen'), modSetWin.destroy)
exit_button.pack(side=LEFT, padx=30, pady=30 )

Das funktioniert bzgl. des Aussehens auch halbwegs; aber sobald man mit der Maus auf einen der Knöpfe fährt, fangen die an wie blöd zu flackern und zu verspringen. Mit einem original Button ist das nicht der Fall. Folglich habe ich vermutlich einen Fehler in meiner Klasse; aber ich komm nicht drauf.

Und 2.:
Seit ich mit der Klassenbildung angefangen habe, gibt es z.T. Probleme mit dem Aufbau von Fenstern. So wird nach dem Schließen eines meiner Fenster der Inhalt des darunter liegenden erst wieder angezeigt wenn man mit der Maus darüberfährt; dies war zuvor nicht der Fall. Von was könnte das herrühren?
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Donnerstag 12. Juni 2003, 13:09

Hi lbuega,

Du scheinst das mit den Klassen und Vererbung noch nicht ganz verstanden zu haben.
Hier erzeugst Du einen Button in in diesem noch einen Button.

Code: Alles auswählen

    def __init__(self, master, text, command):
        Button.__init__(self,master)
        self.button = Button(self, text=text, bd=3, bg=col_um_bg, fg=col_um_fg, font=("arial", 10, "bold"), command=command)
        self.button.pack()
so sollte es gehen.

Code: Alles auswählen

    def __init__(self, master, text, command):
        Button.__init__(self,master, text=text, bd=3, bg=col_um_bg, fg=col_um_fg, font=("arial", 10, "bold"), command=command)
Dein OwnButton ist ja eh schon ein Button, also Du solltest in der Klasse nicht noch einen Button einbauen!
Vermutlich basiert Dein 2. Problem auf einem ähnlichen Fehler.

Gruß

Dookie
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Donnerstag 12. Juni 2003, 13:14

Hi. Ich hab mal versucht deinen Code auszuführen, jedoch gibt es einige Objekte nicht (frame2). Kannst du den bitte auch mal posten?
Dein Problem liegt meiner Meinung nach darin, dass du den Button zwar von "orginal" ableitest, aber die von dir gebildete Instanz in einer Untervariable speicherst (OwnButton.button). Das Programm wird aber denken, dass OwnButton der von ihm anzusprechende Button ist und Commandos an diese Klasse weiterleiten und nicht an OwnButton.button. So kommt in deinem Script das exit_button.pack nie an exit_button.button.pack an. Du musst also entweder eine Weiterleitung in der Klasse implementieren, sodass die Kommandos auch am "Ziel" ankommen oder dir was anderes einfallen lassen.

Ich würde, wenn es dir nur um das aussehen mit den in der init gesetzen Eigenschaften geht, einfach eine entsprechende Funktion schreiben, die dasselbe bewirkt und dir nur das erstellen eines Buttons abnimmt:

Code: Alles auswählen

def own_styled_button(master, text, command):
    b=Button(Button(master, text=text, bd=3, bg=col_um_bg, fg=col_um_fg, font=("arial", 10, "bold"), command=command)
    return b

exit_button = own_styled_button(frame2, 'Abbrechen', modSetWin.destroy)
exit_button.pack(side=LEFT, padx=30, pady=30 )
P.s.: Ich würde auch nicht unbeding import * benutzen... bei vielen Objekten besteht immer das Risiko was zu überschreiben...
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Donnerstag 12. Juni 2003, 13:15

Oh, da war wer schneller... :lol: so gehts natürlich auch, dann kommt alles gleich an ;)
lbuega
User
Beiträge: 75
Registriert: Dienstag 15. April 2003, 08:51
Wohnort: Weissach

Freitag 13. Juni 2003, 07:56

@Dookie, das hab ich auch nie behauptet ;-)

Oh man, das hat einiges Licht ins Dunkel gebracht! Und mein 2. Problem hat damit auch zusammengehangen; allerdings wg. einer anderen Klasse, in der ich aber genauso vorgegangen war. Vielen Dank!

Es werden aber wohl an dieser Stelle noch ein paar Fragen kommen...

hier gleich die erste:
Ich brauche einige Schieber (Scale) die ich horizontal angeordnet habe und davor ein entsprechendes Label. Auch hierfür habe ich mir eine Klasse gebildet.

Code: Alles auswählen

class Schieber(Label, Scale):
    """_('Eigene Klasse >>Schieber<< ... erstellte ein Beschriftung und einen
    zugehörigen Schieberegler
    """
    def __init__(self, master, text, to, tickinterval, resolution=1000):
        Label.__init__(self,master, text=text, bg=col_um_bg, fg=col_um_fg, bd=1, relief=RIDGE, font=("arial", 12, "bold"))
        Scale.__init__(self,master, bd=1, relief=RIDGE, orient=HORIZONTAL, length = 330, from_=0, to=to, \ 
        tickinterval=tickinterval, resolution=resolution, bg=col_um_bg, fg=col_um_fg, font=("arial", 10, "bold"))

# (Fenster, Text, Obergrenze, Scala-Anzeigabstand, Schrittweite)
schieber1=Schieber(modSetWin, _('Max Active Index'), 20000, 5000, 1000)
schieber1.pack()
Der Schieber wird angezeigt; das Label jedoch nicht?!?

Und noch was eher generelles:
Ich habe festgestellt, dass ich meine OwnButton-Klasse auch wie folgt hätte schreiben können (also von "Frame" abgeleitet):

Code: Alles auswählen

class OwnButton(Frame):
    """_('Eigene Klasse >>OwnButton<< abgeleitet von der Python-Klasse
    >>Frame<<; definiert das Aussehen der im Programm verwendeten Buttons')
    """
    def __init__(self, master, text, command):
        Frame.__init__(self,master)
        self.button = Button(self, master, text=text, bd=3, bg=col_um_bg, fg=col_um_fg, font=("arial", 10, "bold"), command=command)
Was ist besser/sinnvoll/richtig???
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Freitag 13. Juni 2003, 14:11

Hallo nochmal :)

zuerstmal zur letzten Frage. Dein OwnButton ist ein "spezialisierter" Button, und sollte daher von Button abgeleitet werden. Dann brauchst Du in der Klasse auch nicht noch ein Buttonobjekt erzeugen wie in Deinem letzten Beispiel.

Jetzt zum neuen Problem.

Code: Alles auswählen

class Schieber(Label, Scale):
    """_('Eigene Klasse >>Schieber<< ... erstellte ein Beschriftung und einen
    zugehörigen Schieberegler
    """
    def __init__(self, master, text, to, tickinterval, resolution=1000):
        Label.__init__(self,master, text=text, bg=col_um_bg, fg=col_um_fg, bd=1, relief=RIDGE, font=("arial", 12, "bold"))
        Scale.__init__(self,master, bd=1, relief=RIDGE, orient=HORIZONTAL, length = 330, from_=0, to=to, \
        tickinterval=tickinterval, resolution=resolution, bg=col_um_bg, fg=col_um_fg, font=("arial", 10, "bold")) 
uiuiui, Mehrfachvererbung hmm verwende ich nie, ist zwar in python möglich, bring aber immer Probleme. Hier hilft wieder die Überlegung was ist Dein Schieber und was hat Dein Schieber? Ist es ein ScaleObjekt mit einem Label oder ein Label mit einem Scaleobjekt? Da die Funktionalität der Scaleobjekts im Vordergrund steht, würde ich zu ersterem Tendieren. Also ein Scaleobjekt mit einem Label.

Code: Alles auswählen

class Schieber(Scale):
    """_('Eigene Klasse >>Schieber<< ... erstellte einen Schieberegler mit
    zugehöriger Beschriftung
    """
    def __init__(self, master, text, to, tickinterval, resolution=1000):
        self.label = Label(master, text=text, bg=col_um_bg, fg=col_um_fg, bd=1, relief=RIDGE, font=("arial", 12, "bold"))
        self.label.pack()
        Scale.__init__(self,master, bd=1, relief=RIDGE, orient=HORIZONTAL, length = 330, from_=0, to=to, \
        tickinterval=tickinterval, resolution=resolution, bg=col_um_bg, fg=col_um_fg, font=("arial", 10, "bold")) 
So kannst Du Deine Schieberobjekte genauso verwenden und abfragen wie Scaleobjekte. In dem Falle, da ein Label ja kaum geändert oder abgefragt wird, würde ich es so machen. Wenn Du complexere zusammenarbeitende Objekte zusammenfassen willst, würde ich diese dann von einem Frame ableiten und in den Frame die objekte Packen und Anfragen und Auswertungen an die Unterobjekte weiterleiten. Z.B. bei einem Scale und einem Textobjekt, in dem der Wert des Scaleobjektes auch numerisch eingegeben werden kann. Ich bastel dann später mal ein Beispiel und poste es hier.


Gruß

Dookie
lbuega
User
Beiträge: 75
Registriert: Dienstag 15. April 2003, 08:51
Wohnort: Weissach

Freitag 13. Juni 2003, 15:39

Naja, man wird eben mit der Zeit mutiger... Aber hat leider nicht wirklich geklappt. Habs dann versucht von "Frame" abzuleiten, aber dann fehlen ja die ganzen Funktionen wie z.B. "set" oder "get"

Gibt es eigentlich auch eine einfachere Möglichkeit wie Klassenbildung um das Aussehen von Objekten zu beeinflußen ? Da ich alle gleichen Objekte auch identisch haben möchte ohne jedem einzelnen Objekt sagen zu müßen wie es auszusehen hat, hab ich in meiner Verzweiflung auf Klassen zurückgegriffen. Allerdings wird dies so langsam aber sicher unübersichtlich, da ich ja für alle benutzten Widgets eigene Klassen ableite (wie z.B. bereits oben mit der "OwnButton"-Klasse)!?!
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Freitag 13. Juni 2003, 17:08

Die Objektklassen von Tkinter haben auch einen Parameter cnf, diesem kannst Du ein Dictionary übergeben, welches Deine Basisconfigurationen für die Widgets enthält. Hier gleich in meinem Beispiel realisiert.

Code: Alles auswählen

from Tkinter import *

col_um_bg = "black"
col_um_fg = "white"

""" erzeuge Dictionary mit den Grundeinstellungen der Widgets """
my_config = {"bg" : col_um_bg, "fg" : col_um_fg, "font" : ("arial", 10, "bold")}

class Schieber(Frame):
    """ Kombination aus Scale und Entry """
    def __init__(self, master, variable=None):
        if variable != None:
            self.contents = variable
        else :
            self.contents = StringVar()
            self.contents.set("0")
        
        Frame.__init__(self, master)
        self.scale = Scale(self,
                           my_config,
                           orient=HORIZONTAL,
                           showvalue=0,
                           variable=self.contents)
        self.scale.pack(side=LEFT)
        self.entry = Entry(self, my_config, width=3, textvariable=self.contents)
        self.entry.pack(side=LEFT)
    
    def get(self):
        return self.contents.get()

    def set(self, value):
        self.contents.set(value)
    
root = Tk()
mySchieber = Schieber(root)
mySchieber.pack()
mySchieber.set(50)
root.mainloop()
Die Basisconfigurationen für die Widgets könntest Du auch aus einer (XML)Datei in verschiedene Dictionarys einlesen.

Gruß

Dookie
lbuega
User
Beiträge: 75
Registriert: Dienstag 15. April 2003, 08:51
Wohnort: Weissach

Montag 16. Juni 2003, 14:09

Jetzt weiss ich endlich auch mal für was dieses "cnf" steht :idea: (configuration). Ist ja wirklich super praktisch, wenn man das alles bequem in ein Dictionary packen kann! :D

Aber bei dieser Art der Ableitung/Vererbung (von "Frame") kommt man dann wohl doch nicht um das Neuschreiben der benötigten Funktionen wie z.B. "set", "bind", usw. aus :? - richtig?

Das automatisch aktualisierende Verhalten des Entry ist spitze!

Genau so etwas benötige ich noch im Zusammenhang mit "Listbox": (Leider scheint es hier keine "variable"-Option zu geben; über "curselection()" krieg ich's nur in Verbindung mit nem extra Button hin; und ein "bind" auf die Listbox hat bei mir nicht bzw. nur unzureichend funktioniert - hat immer den zuvor ausgewählten Wert geliefert).
Es soll quasi bei Klick auf einen der Werte in der "Listbox" dieser sofort in nem "Label" (oder "Entry") angezeigt werden und zusätzlich soll der Inhalt einer 2. Listbox abhängig vom gewählten Wert aufgebaut werden.
lbuega
User
Beiträge: 75
Registriert: Dienstag 15. April 2003, 08:51
Wohnort: Weissach

Dienstag 17. Juni 2003, 13:45

Hallo,
habe für einen Teil meiner Frage bzgl. der Listboxen in folgedem Tkinter-Tutorial etwas (gutes) gefunden, dass ich aber leider dennoch nicht verstehe - kann mir da jmd. auf die Sprünge helfen? http://www-md.fsl.noaa.gov/eft/develope ... stbox.html
(speziell ab: "Unfortunately, the listbox doesn't provide a command option allowing you to track changes to the selection...")
- wie muss ich das mit dem "bind" verstehen ("self.ok" ?), und
- wie funktioniert das "poll"
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Dienstag 17. Juni 2003, 14:50

is wohl wieder mal nen unvollständiges Beispiel, so wie ich das lese bezieht sich self.ok auf einen ok-button der der Listbox angefügt ist und dessen Funktion über den Doubleklick aufruft. Wenn Du einen Einzelklick für die Auswahl willst, nimm besser <ButtonRelease-1> das liefert dann bei der Abfrage wirklich die aktelle Selektion.

Beispiel:

Code: Alles auswählen

from Tkinter import *
labels = ["Alpha", "Beta", "Gamma", "Delta"]

class Listbox_plus(Listbox):
    """ Kombination von Listbox und Entry """
    def __init__(self, master, cnf={}, variable=None, labels=[], **kw):
        if variable != None:
            self.contents = variable
        else :
            self.contents = StringVar()
        Listbox.__init__(self, master, cnf, **kw)
        self.entry = Label(master, width=12, textvariable=self.contents)
        self.delete(0, END)
        for i in labels:
            self.insert(END, i)
        self.bind("<ButtonRelease-1>", self._select)

    def _select(self, event):
        current = self.curselection()
        value = self.get(current[0])
        self.contents.set(value)
        
    def pack(self, cnf={}, **kw):
        Listbox.pack(self, cnf, **kw)
        self.entry.pack(cnf, **kw)

root = Tk()myList = Listbox_plus(root, labels=labels)
myList.pack()
root.mainloop()
das poll funktioniert so, daß die Methode immer wieder aufgerufen wird.

Code: Alles auswählen

def poll(self):
    now = self.list.curselection()     // aktuelle selektion aus liste holen
    if now != self.current:              // aktuelle selektion != alte selektion
        self.list_has_changed(now)  // mach was wenn es sich geändert hat
        self.current = now               // aktuelle selektion merken
    self.after(250, self.poll)            // schau in 250ms nochmal nach
Ich würde es aber eher ereignisgesteuert mit bind machen.


Gruß

Dookie
Antworten