Seite 1 von 2

Mehrere entry-widgets gleichzeitig konfigurieren

Verfasst: Freitag 5. Februar 2010, 19:32
von heiliga horsd
Hallo,

ich bräuchte mal wieder eure Hilfe, und zwar habe ich in einem Programm 11 entry-widgets, die alle gleich konfiguriert werden müssen. Gibts da ne kurze und knackige Möglichkeit (die widgets heißen bspw "entry_blabla1" etc.), alle auf einmal zu konfigurieren oder muss ich das für jedes einzeln machen? Es heißt ja immerhin auch "Don't repeat yourself" oder?

Lg HH

Verfasst: Freitag 5. Februar 2010, 19:46
von Dav1d
Vorneweg, ich habe keine Ahnung von tkindter und von den Widgets,
Du könntest Instanzen von jedem wifget in einer Liste speichern, und dann mit einer for-Schleife über die Liste iterieren und dann jedes Widget einzeln (in der For-Schleife) konfigurieren und / oder du erstell die Widgets gleich mit einer For-Schleife dann kannst du dir den Umweg über eine Liste sparen

Verfasst: Freitag 5. Februar 2010, 19:58
von heiliga horsd
Hatte ich anfangs auch überlegt, jedoch dann schnell wieder verworfen, aber wenn ich das hier auch als Tipp bekomme, kann es wohl nicht so schlecht gewesen sein, ich schau mir das morgen mal an (außer es hat noch jemand eine Idee).

Danke dir!


Lg HH

Verfasst: Samstag 6. Februar 2010, 09:58
von wuf
Hallo heiliga horsd

Hier eine von vielen Varianten:

Code: Alles auswählen

# python3.1
# wuf_ref: py31_multiple_entries_02.py

import tkinter as tk

NUM_OF_ENTRIES = 11
ENTRY_BG_COLOR = 'white'

def enter_value(event):
    entry_obj = event.widget
    print(entry_obj.get())

app_win=tk.Tk()

entry_obj_dict = dict()

for entry_index in range(NUM_OF_ENTRIES):
    entry_obj = tk.Entry(app_win, bg=ENTRY_BG_COLOR)
    entry_obj.pack(padx=5, pady=2)
    entry_obj.bind('<Return>', enter_value)
    entry_key = 'entry_blabla_'+str(entry_index)
    entry_obj.insert(0, entry_key)
    entry_obj_dict[entry_key] = entry_obj

print(entry_obj_dict['entry_blabla_0'].get())

entry_obj_dict['entry_blabla_10'].config(bg='mistyrose')
entry_obj_dict['entry_blabla_10'].delete(0, 'end')
entry_obj_dict['entry_blabla_10'].insert(0, 'Meine Eingabe')

app_win.mainloop()
Gruss wuf :wink:

Verfasst: Samstag 6. Februar 2010, 12:47
von heiliga horsd
Super, funktioniert einwandfrei! Danke!!

Verfasst: Samstag 6. Februar 2010, 13:37
von EyDu
Das ist doch nur umständlich eine Liste nachgebaut. Wenn man Namen durchnummeriert, dann macht man meistens etwas falsch und sollte gleich eine Liste benutzen.

Verfasst: Samstag 6. Februar 2010, 14:13
von wuf
Hallo EyDu
EyDu hat geschrieben:dann macht man meistens etwas falsch
Ich respektiere deine Auffassung. Meine ist halt eine andere. Vielleicht kannst du deine Gedanken hier auch in Codeform präsentieren dann profitieren wir alle davon.

Danke. Gruss wuf :wink:

Verfasst: Samstag 6. Februar 2010, 14:17
von Dav1d
Ich galub das kann ich auch

Code: Alles auswählen

# python3.1
# wuf_ref: py31_multiple_entries_02.py

import tkinter as tk

NUM_OF_ENTRIES = 11
ENTRY_BG_COLOR = 'white'

def enter_value(event):
    entry_obj = event.widget
    print(entry_obj.get())

app_win=tk.Tk()

entry_obj_list= list()

for entry_index in range(NUM_OF_ENTRIES):
    entry_obj = tk.Entry(app_win, bg=ENTRY_BG_COLOR)
    entry_obj.pack(padx=5, pady=2)
    entry_obj.bind('<Return>', enter_value)
    entry_key = 'entry_blabla_'+str(entry_index)
    entry_obj.insert(0, entry_key)
    entry_obj_list.append(entry_obj)

print(entry_obj_list[0].get())

entry_obj_list[10].config(bg='mistyrose')
entry_obj_list[10].delete(0, 'end')
entry_obj_list[10].insert(0, 'Meine Eingabe')

app_win.mainloop() 

Verfasst: Samstag 6. Februar 2010, 14:20
von yipyip
EyDu hat natuerlich recht. Hier noch ein Pattern:

Code: Alles auswählen

#!/usr/bin/env python3.1

####

import tkinter as tk

####

class Entries(object):

   
  def __init__(self, names, bg='black', fg='green', hl='red'):

    self.root = tk.Tk()
    self.svars = [tk.StringVar() for _ in names]         
    self.entries = [tk.Entry(self.root, textvariable=v, width=20,
                             bg=bg, fg=fg, highlightcolor=hl)
                    for v in self.svars]

    self.labels = [tk.Label(self.root, text=name) for name in names]
    for i, (label, entry) in enumerate(zip(self.labels, self.entries)):
      entry.bind('<Return>', lambda ev, i=i: self.doit(i))
      label.grid(column=0, row=i)
      entry.grid(column=1, row=i)
                    
      
  def doit(self, i):

    content = self.svars[i].get()
    print(content)
    

  def run(self):

    self.root.mainloop()

####

if __name__ == '__main__':

  MAXI = 11
  Entries(['Text%02d' % i for i in range(MAXI)]).run() 
:wink:
yipyip

Verfasst: Samstag 6. Februar 2010, 14:34
von wuf
@Dav1d: Ist eine billige Kopie wie sie im fernen Osten praktiziert wird.

@yipyip: Danke für deinen Beitrag: Super! Ist einer der vielen möglichen Varianten.

Gruss wuf :wink:

Verfasst: Samstag 6. Februar 2010, 14:43
von Dav1d
@wuf das war Absicht!

Verfasst: Samstag 6. Februar 2010, 14:47
von wuf
@Dav1d: Das galub ich dir. Habe es auch so aufgefasst. :lol:

Gruss wuf :wink:

Verfasst: Samstag 6. Februar 2010, 15:12
von Dav1d
Gut ;)

Mir gings nur darum eine 2. Möglichkeit zu zeigen und zwar mit einer Liste statt einem Dict aufgrund dem hier:
EyDu hat geschrieben:Das ist doch nur umständlich eine Liste nachgebaut. Wenn man Namen durchnummeriert, dann macht man meistens etwas falsch und sollte gleich eine Liste benutzen.

Verfasst: Samstag 6. Februar 2010, 16:19
von wuf
Hallo Dav1d

Im ersten Moment sah es wirklich so aus wie eine 1:1 Kopie, da die Zeilenanzahl die genau gleiche ist. Somit haben wir die dict- und list-Variante. OK Dav1d danke noch für deine Variante.

Wünsche dir noch ein schönes WE.

Gruss wuf :wink:

Verfasst: Samstag 6. Februar 2010, 17:37
von heiliga horsd
Hallo yipyip, Danke für deinen Vorschlag! Er begeistert mich insofern, dass ich auch grade versuche, das Programm Objektorientiert zu schreiben und ich verschiedene namen verwenden kann. Jedoch verstehe ich einige Passagen aus dem Quelltext noch nicht so ganz und ich wäre über eine Erklärung daher sehr erfreut!

Code: Alles auswählen

#!/usr/bin/env python3.1

####    # Hat das irgendeinen tieferen Sinn?

import tkinter as tk

####

class Entries(object):

   
  def __init__(self, names, bg='black', fg='green', hl='red'):

    self.root = tk.Tk() # MUSS es root heißen?
    self.svars = [tk.StringVar() for _ in names]    # Hat der Unterstrich eine spezielle Bedeutung?     
    self.entries = [tk.Entry(self.root, textvariable=v, width=20,
                             bg=bg, fg=fg, highlightcolor=hl)
                    for v in self.svars]

    self.labels = [tk.Label(self.root, text=name) for name in names]
    for i, (label, entry) in enumerate(zip(self.labels, self.entries)): # Könntest du bitte die ganze Zeile ein wenig erläutern?
      entry.bind('<Return>', lambda ev, i=i: self.doit(i)) # Den lambda-Ausdruck in diesem Falle verstehe ich auch nicht so ganz :(
      label.grid(column=0, row=i)
      entry.grid(column=1, row=i)
                   
     
  def doit(self, i):

    content = self.svars[i].get()
    print(content)
   

  def run(self):

    self.root.mainloop()

####

if __name__ == '__main__':

  MAXI = 11
  Entries(['Text%02d' % i for i in range(MAXI)]).run() # Was hat >>'Text%02d' % i<< zu bedeuten?

Entschuldigung für die wahrscheinlich dummen Fragen, aber ich arbeite das erste mal tiefer mit Objektorientierter Programmierung und GUIs und befinde mich auch noch allgemein in der Lernphase.

Lg HH

Verfasst: Samstag 6. Februar 2010, 17:50
von EyDu
Hallo.

a) Muss es root heißen? Nein, es bietet sich nur an.
b) Unterstrich: Hier nicht. Man spart sich lediglich einen Namen auszudenken, da ein solcher hier nicht gebracht wird.
c) Zum Rest: Probiere die Sachen doch mal im interaktiven Iterpreter aus und lese ein wenig in der Dokumentation. Das sind alles keine Dinge, die man nicht innerhalb von 30 Minuten selber rausfinden kann.

Sebastian

Verfasst: Samstag 6. Februar 2010, 17:51
von Dav1d
Zum 1. Ich denke das hat keinen tieferen Sinn ;)

2. Es muss nicht root heißen
3. Unterstriche verwendet man, wenn man die Variable (Bezeichner) nicht braucht
4. Ich hoffe der Code hilft

Code: Alles auswählen

>>> zip([1,2,3,4], [5,6,7,8])
[(1, 5), (2, 6), (3, 7), (4, 8)]
>>> for i, b in enumerate(['a', 'b', 'c']):
	print 'n-ter Buchstabe:', i
	print 'Buchstabe:', b

	
n-ter Buchstabe: 0
Buchstabe: a
n-ter Buchstabe: 1
Buchstabe: b
n-ter Buchstabe: 2
Buchstabe: c 
5. Den Lambdaausdruck kannst du mit

Code: Alles auswählen

def enter_value(event):
    entry_obj = event.widget
    print(entry_obj.get())
vergleichen

//Edit schon wieder zu langsam :evil:

Verfasst: Samstag 6. Februar 2010, 18:17
von heiliga horsd
Danke euch beiden!
Ich versuche nun das ganze nochmal in Worten auszudrücken, um zu sehen, ob ich es verstanden habe:

1) Ich könnte statt "root" auch "Fernseher" benutzen, wenn ich Lust darauf hätte
2) Statt einem Unterstrich (_) könnte ich auch ein anderes Zeichen verwenden (bspw. ein "w")
3) zip nimmt als Argumente Listen entgegen und gibt dann eine neue Liste aus. Die neue Liste enthält Tupel und in jedem Tupel befindet sich das n-te Objekt der einzelnen (alten) Listen.
4) enumerate zählt letzendlich bloß durch
5) Der lambda-Ausdruck sorgt bei einem bestimmten Event (hier Enter-Taste) dafür, dass der Inhalt des Entry-Feldes ausgegeben wird.

Alles richtig verstanden?

Verfasst: Samstag 6. Februar 2010, 18:28
von Dav1d
Fast alles, in diesem Fall ruft der Lambdaausdruck nur die Funktion doit mit einem bestimmten Parameter auf, wenn man statt dem Lambdaasudruck nur seld.doit schreibt, wird self.doit() mit dem Eventparameter der von tkinter kommt aufgerufen

Verfasst: Samstag 6. Februar 2010, 18:35
von EyDu
Hallo!

1) Ja, du kannst Namen beliebig wählen, so lange sie nicht mit einem reservierten Wort kollidieren (wie "for", "if", ...). Außerdem solltest du keine buil-in-Namen verdecken ("list", "dict", "set", ...)
2) Ja, jeden zulässigen Namen
3) Fast richtig: es können nicht nur Listen entgegengenommen werden, sonder alles über das iteriert werden kann. Schau dir auch noch den Sonderfall an, wenn nicht alle Listen die selbe Länge haben
4) Mal etwas anders ausgedrück: enumerate liefert zu jedem Wert noch seinen Index (den Start kann man beliebig einstellen). Auch hier kann wieder alles übergeben werden, über das auch iteriert werden kann
5) Das ist das, was am Schluß rauskommt. Ein lambda-Ausdruck ist eigentlich eine anonyme Funktion. Also eine Funktion ohne Namen. Jeden lambda-Ausdruck kannst du also auch in eine Funktion übersetzen:

Code: Alles auswählen

lambda x : x = 2*x

def function(x):
    return 2*x
@yipyip: Das geht aber noch ein wenig schöner. Mal ein Vorschlag:

Code: Alles auswählen

import Tkinter as tk

MAXI = 11

class Entries(object):
    def __init__(self, names, **keys):
        self.root = tk.Tk()

        for row, name in enumerate(names):
            var = tk.StringVar()
            entry = tk.Entry(self.root, textvariable=var, **keys)
            label = tk.Label(self.root, text=name)

            label.grid(column=0, row=row)
            entry.grid(column=1, row=row)
            entry.bind('<Return>', lambda e, v=var: self.doit(e, v.get()))
     
    def doit(self, event, var):
        print var

    def run(self):
        self.root.mainloop()

if __name__ == '__main__':
    Entries(map('Text{0:02d}'.format, range(MAXI)), width=20).run()