Seite 1 von 2
ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Sonntag 22. Juni 2014, 10:21
von Nobuddy
Hallo zusammen,
seit Kurzem beschäftige ich mich mit der ComboBox von tkinter.ttk.
Ich versuche eine Lösung zu finden, bei der im Ausgabefeld der ComboBox, die Eingabe des Zeichens überprüft und dann gleichzeitig das Ein- Ausgabefeld der Combobox, mit einem in der Liste vorkommenden Ergebnis ausgefüllt wird.
Teilweise ist mir das gelungen, jedoch bleibt immer die erste Eingabe eines Zeichens zuerst nicht berücksichtigt.
Ich poste hier mal meinen Testcode:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x
import tkinter as tk
import tkinter.ttk as ttk
def action(event):
"""
a combo box item has been selected, do some action
"""
myentry = combo.get()
if not myentry:
return event.keysym
print('0', myentry)
sign_count = len(myentry)
while not [combo.set(x) for x in L
if x[:sign_count+1].startswith(myentry)]:
combo.set('')
return
combo.set(combo.get())
print('1', combo.get())
def output(event):
combo.set(combo.get()[1:])
print('Result: ', combo.get())
my_win = tk.Tk()
frame = tk.Frame(my_win)
frame.pack(side='top', expand=True)
# create the combo box
combo = ttk.Combobox()
combo.bind('<<ComboboxSelected>>', action)
combo.bind('<KeyPress>', action)
combo.bind('<Return>', output)
combo.focus_set()
L = ['aaaaaa', 'abbbbb', 'aaabbb', 'acabbb', 'bbbbbb', 'cccccc',
'vvvvvv', 'wwwwww', 'xxxxxx', 'yyyyyy', 'yywyyy', 'zzzzzz', ]
# load the combo box with the list
combo['values'] = L
# pack the widgets vertically in this order
#entry.pack(fill='both', expand='yes')
combo.pack()
my_win.mainloop()
Ich hoffe, Ihr könnt mir dabei weiterhelfen!
Grüße Nobuddy
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Sonntag 22. Juni 2014, 12:04
von BlackJack
@Nobuddy: Die ``while``-Schleife ist falsch. Aber so etwas von. Es ist eine ”Schleife”, die wenn der Körper betreten wird mit einem ``return`` endet — Schleifen die grundsätzlich maximal einmal ausgeführt werden sind keine Schleifen. Die „list comprehension” wird missbräuchlich eingesetzt, denn die Elemente die dort produziert werden, interessieren überhaupt nicht. Und sie sind das Ergebnis des `combo.set()`-Aufrufs, der in einer Schleife steht, aber warum, denn einen tatsächlichen Effekt hat ja immer nur der jeweils letzte Aufruf, also kann man sich alle davor sparen.
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Sonntag 22. Juni 2014, 12:29
von Nobuddy
Hallo BlackJack,
zu dem Ergebnis, bin ich auch gekommen.
Bin gerade dabei dies zu ändern und poste es danach!
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Sonntag 22. Juni 2014, 13:26
von Nobuddy
Hier der aktualisierte Code:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x
import tkinter as tk
import tkinter.ttk as ttk
def action(event):
"""
a combo box item has been selected, do some action
"""
try:
check = [x for x in L if x.startswith(combo.get())][0]
res_count = len(check)
combo.set(check[1:res_count + 1])
except IndexError:
combo.set('')
def output(event):
combo.set(combo.get())
print('Result: ', combo.get())
my_win = tk.Tk()
frame = tk.Frame(my_win)
frame.pack(side='top', expand=True)
# create the combo box
combo = ttk.Combobox()
combo.bind('<<ComboboxSelected>>', action)
combo.bind('<KeyPress>', action)
combo.bind('<Return>', output)
combo.focus_set()
L = ['aaaaaa', 'abbbbb', 'aaabbb', 'acabbb', 'bbbbbb', 'cccccc',
'vvvvvv', 'wwwwww', 'xxxxxx', 'yyyyyy', 'yywyyy', 'zzzzzz', ]
# load the combo box with the list
combo['values'] = L
# pack the widgets vertically in this order
#entry.pack(fill='both', expand='yes')
combo.pack()
my_win.mainloop()
Aber immer noch weit vom Ziel entfernt.
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Sonntag 22. Juni 2014, 15:20
von Schorlem
action() ist zwar hässlich wie sonst was, funktioniert aber. Nur mal als Anregung. Der Ansatz hier ist die Unterscheidung von Benutzereingabe und den vom Computer hinzugefügten Zeichen. Zusätzlich habe ich das Event von <KeyPress> auf <KeyRelease> geändert, damit die Combobox korrekt aktualisiert wird. Der Rest ist klar
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x
import tkinter as tk
from tkinter import ttk
class Ttest:
def __init__(self):
self.my_win = tk.Tk()
self.frame = tk.Frame(self.my_win)
self.frame.pack(side='top', expand=True)
# create the self.combo box
self.combo = ttk.Combobox()
self.combo.bind('<<self.comboboxSelected>>', self.action)
self.combo.bind('<KeyRelease>', self.action)
self.combo.bind('<Return>', self.output)
self.combo.focus_set()
self.L = ['aaaaaa', 'abbbbb', 'aaabbb', 'acabbb', 'bbbbbb', 'cccccc',
'vvvvvv', 'wwwwww', 'xxxxxx', 'yyyyyy', 'yywyyy', 'zzzzzz', ]
# load the self.combo box with the list
self.combo['values'] = self.L
# pack the widgets vertically in this order
#entry.pack(fill='both', expand='yes')
self.combo.pack()
self.user_input = ""
def action(self, event):
if event.char == "":
self.user_input = self.user_input[0:-1]
else:
self.user_input = self.user_input + event.char
if self.combo.get().strip() == "":
self.user_input = ""
if self.user_input == "":
return
for item in self.L:
if item.startswith(self.user_input):
self.combo.set(self.user_input + item[len(self.user_input):])
def output(self, event):
self.combo.set(self.combo.get())
print('Result: ', self.combo.get())
def _main():
_ = Ttest()
if __name__ == "__main__":
_main()
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Sonntag 22. Juni 2014, 17:41
von Nobuddy
Hallo Schorlem,
Danke für Deinen Input!
Habe Deinen Code leicht abgeändert und Schönheitsfehler behoben.
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x
from tkinter import ttk
class ComboBox:
def __init__(self, mylist):
self.mylist = mylist
# create the self.combo box
self.combo = ttk.Combobox()
self.combo.bind('<<self.comboboxSelected>>', self.action)
self.combo.bind('<KeyRelease>', self.action)
self.combo.bind('<Return>', self.output)
self.combo.focus_set()
# load the self.combo box with the list
self.combo['values'] = self.mylist
# pack the widgets vertically in this order
self.combo.pack()
self.user_input = ''
self.combo.mainloop()
def action(self, event):
if event.char == '':
print('000')
self.user_input = self.user_input[0:-1]
else:
print('111')
self.user_input = self.user_input + event.char
if self.combo.get().strip() == '':
return
total_sign = ''
for item in self.mylist:
if item.startswith(self.user_input):
total_sign = self.user_input + item[len(self.user_input):]
if item == total_sign:
self.combo.set(total_sign)
if total_sign == '':
if [True for item in self.mylist
if item.startswith(event.char)]:
self.user_input = event.char
self.combo.set(self.user_input)
else:
self.combo.set('')
def output(self, event):
self.combo.set(self.combo.get())
print('Result: ', self.combo.get())
def main():
L = ['aaaaaa', 'abbbbb', 'aaabbb', 'acabbb', 'bbbbbb', 'cccccc',
'vvvvvv', 'wwwwww', 'xxxxxx', 'yyyyyy', 'yywyyy', 'zzzzzz', ]
ComboBox(L)
if __name__ == "__main__":
main()
Bei:
Code: Alles auswählen
def action(self, event):
if event.char == '':
print('000')
self.user_input = self.user_input[0:-1]
konnte ich nicht feststellen, daß es gebraucht wird.
In welchem Fall würde dies greifen?
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Sonntag 22. Juni 2014, 17:57
von Schorlem
Nobuddy hat geschrieben:Bei:
Code: Alles auswählen
def action(self, event):
if event.char == '':
print('000')
self.user_input = self.user_input[0:-1]
konnte ich nicht feststellen, daß es gebraucht wird.
In welchem Fall würde dies greifen?
Der leere String wird zurückgegeben, wenn die Back-Taste gedrückt wurde. Danach wird der letzte Buchstabe "gelöscht" (eigentlich wird ja nur alles außer dem letzten Buchstaben "user_input" neu zugewiesen).
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Sonntag 22. Juni 2014, 18:16
von BlackJack
@Schorlem: Nicht nur bei Backspace sondern auch bei allen möglichen anderen Tasten die keinem sichtbaren Zeichen entsprechen. Versuch zum Beispiel mal den Cursor innerhalb des Eingabefeldes nach rechts oder links zu versetzen. Das sieht mir ziemlich kaputt aus alles was kein Zeichen ist als Backspace zu behandeln.
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Sonntag 22. Juni 2014, 18:21
von Nobuddy
OK, habe verstanden.
Wenn ich aber mehrmals die Back-Taste verwende, werden auf einmal Vierecke in das Feld eingefügt.
Was mich interessiert ist:
- Ist es möglich das Modul in ein anderes externes Fenster zu integrieren, in ein canvas-Feld z.B.?
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Sonntag 22. Juni 2014, 19:26
von Schorlem
Nobuddy hat geschrieben:OK, habe verstanden.
Wenn ich aber mehrmals die Back-Taste verwende, werden auf einmal Vierecke in das Feld eingefügt.
Wat. Passiert bei mir jedenfalls nicht. o.O
Nobuddy hat geschrieben:Ist es möglich das Modul in ein anderes externes Fenster zu integrieren, in ein canvas-Feld z.B.?
Mit Canvas.create_window geht's. Ich kenn mich selber damit nicht so aus, mehr darüber findest du
hier
@BlackJack Oh ja, vielen Dank, dass du's erwähnst^^ Einfach
mit
ersetzen.
Ich habe übrigens die ganze "if total_sign == '':" samt Schleife rausgeworfen, der Sinn hat sich mir nicht wirklich erschlossen. Zusätzlich ist die "if item == total_sign"-Verzweigung recht sinnlos, integrier die eine Zeile in der Verzweigung darüber.
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Montag 23. Juni 2014, 08:39
von Nobuddy
Mit der Änderung: 'if event.keysym == "BackSpace":', funktioniert es wie ich es mir vorgestellt habe.
Allerdings, ist das mi 'total_sign' nicht unnötig.
In 'action', nach der for-Schleife, erfüllt 'if total_sign == '':' eine wichtige Aufgabe.
Hier der aktuell funktionierende Code:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x
from tkinter import ttk
class ComboBox:
def __init__(self, mylist):
self.mylist = mylist
# create the self.combo box
self.combo = ttk.Combobox()
self.combo.bind('<<self.comboboxSelected>>', self.action)
self.combo.bind('<KeyRelease>', self.action)
self.combo.bind('<Return>', self.output)
self.combo.focus_set()
# load the self.combo box with the list
self.combo['values'] = self.mylist
# pack the widgets vertically in this order
self.combo.pack()
self.user_input = ''
self.combo.mainloop()
def action(self, event):
if event.keysym == 'BackSpace':
self.user_input = self.user_input[0:-1]
else:
self.user_input = self.user_input + event.char
if self.combo.get().strip() == '':
return
total_sign = ''
for item in self.mylist:
if item.startswith(self.user_input):
total_sign = self.user_input + item[len(self.user_input):]
if item == total_sign:
self.combo.set(total_sign)
if total_sign == '':
if [True for item in self.mylist
if item.startswith(event.char)]:
self.combo.set(self.user_input)
else:
self.combo.set('')
def output(self, event):
self.combo.set(self.combo.get())
print('Result: ', self.combo.get())
def main():
L = ['aaaaaa', 'abbbbb', 'aaabbb', 'acabbb', 'bbbbbb', 'cccccc',
'vvvvvv', 'wwwwww', 'xxxxxx', 'yyyyyy', 'yywyyy', 'zzzzzz', ]
ComboBox(L)
if __name__ == "__main__":
main()
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Dienstag 24. Juni 2014, 13:57
von Nobuddy
Der letzte gepostete Code, funktionierte auch nicht so, wie von mir angedacht.
Nach einigem Testen, habe ich das Ganze soweit, daß in allen Ausführungen die ComboBox richtig funktioniert.
Der Code hat sich etwas aufgebläht ..., aber vielleicht fallen Euch dazu Abkürzungen ein!
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x
from tkinter import ttk
class ComboBox:
def __init__(self, mylist):
self.mylist = mylist
self.event_check = {'??' : '??', 'Return' : 'Return',
'Delete' : 'Delete', 'KP_Enter' : 'KP_Enter',
'KP_Delete' : 'KP_Delete'}
self.key2utf = {'adiaeresis' : 'ä', 'Adiaeresis' : 'Ä',
'odiaeresis' : 'ö', 'Odiaeresis' : 'Ö', 'udiaeresis' : 'ü',
'Udiaeresis' : 'Ü', 'ssharp' : 'ß', 'space' : ' '}
# create the self.combo box
self.combo = ttk.Combobox()
self.combo.bind('<<ComboboxSelected>>', self.control)
self.combo.bind('<KeyRelease>', self.control)
self.combo.focus_set()
# load the self.combo box with the list
self.combo['values'] = self.mylist
# pack the widgets vertically in this order
self.combo.pack()
self.key_input = dict()
self.combo.mainloop()
def control(self, event):
try:
if tuple(self.key_input.keys())[0] == 0:
self.key_input = dict()
self.combo.set('')
except IndexError:
pass
tkey = self.key2utf.get(event.keysym)
if len(event.keysym) == 1 or tkey:
kip = event.keysym
if tkey:
kip = tkey
try:
lek = tuple(self.key_input.keys())[0]
lek_res = self.key_input.get(lek)
if lek_res:
new = lek_res + kip
counter = lek + 1
self.key_input.update({counter : new})
del self.key_input[lek]
except IndexError:
self.key_input.update({1 : kip})
try:
if (event.keysym == 'BackSpace' or event.keysym == 'Delete'
or event.keysym == 'KP_Delete'):
if len(self.combo.get()) > 0:
try:
if tuple(self.key_input.keys())[0] > 0:
new = dict([((key - 1),
self.key_input[key][:key+1])
for key in self.key_input])
self.key_input = dict()
self.key_input.update(new)
try:
self.combo.set(tuple(
self.key_input.values())[0])
except IndexError:
pass
except IndexError:
self.key_input = dict()
else:
self.key_input = dict()
self.combo.set('')
return
if self.event_check.get(event.keysym):
len(self.combo.get())
self.key_input = dict()
self.key_input.update(
{len(self.combo.get()) : self.combo.get()})
self.combo.set(self.combo.get())
self.myresult = self.combo.get()
return self.myresult
except AttributeError:
pass
self.combo.set(self.combo.get().strip())
if self.combo.get() == '':
return
try:
comparison = tuple(self.key_input.values())[0]
except IndexError:
return
try:
check = [item for item in self.mylist
if item.startswith(comparison)][0]
self.total_sign = comparison + check[
len(comparison):].strip()
if check == self.total_sign:
self.combo.set(check)
self.myresult = self.combo.get()
return self.myresult
new = dict([((key - 1), self.key_input[key][0:-1])
for key in self.key_input])
self.key_input = dict()
self.key_input.update(new)
self.combo.set(tuple(self.key_input.values())[0])
self.myresult = ''
return self.myresult
except IndexError:
new = dict([((key - 1), self.key_input[key][0:key-1])
for key in self.key_input])
self.key_input = dict()
self.key_input.update(new)
self.combo.set(tuple(self.key_input.values())[0])
self.myresult = ''
return self.myresult
def main():
L = ['aaaaaa', 'abbbbb', 'aaabbb', 'acabbb', 'bbbbbb', 'cccccc',
'vvvvvv', 'wwwwww', 'xxxxxx', 'yyyyyy', 'yywyyy', 'zzzzzz', ]
ComboBox(L)
if __name__ == "__main__":
main()
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Dienstag 24. Juni 2014, 14:34
von EyDu
Als erstes solltest du deinen Code in sinnvolle Funktionen/Methoden aufteilen. Als Faustregel gilt, dass eine Funktion nicht länger sein sollte als eine Bildschirmseite. Sonst wird es unübersichtlich, bzw. unleserlich.
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Dienstag 24. Juni 2014, 14:40
von Nobuddy
Hallo EyDu,
da gebe ich Dir völlig Recht, daran muß ich noch arbeiten.
Werde es nochmals überarbeiten.
Wenn Du dazu noch weitere Vorschläge hast, nur her damit!
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Dienstag 24. Juni 2014, 14:40
von Hyperion
Zudem sollte man darauf achten, nicht zu viele Einrückungsebenen in einem Code-Abschnitt zu haben; Zeile 57ff. ist da ein Negativ-Beispiel! Fünf Einrückungen *innerhalb* einer Methode sind einfach zu viel. Neben der reinen Anzahl an Zeilen ist das auch ein brauchbarer Indikator.
Ich nehme an, dass Du Dich mit dem Thema Unit-Testing noch nicht auseinander gesetzt hast? Spätestens dabei lernt man, dass man so eine Funktion niemals sinnvoll testen kann, da man kaum mehr alle Logik-Äste überblicken kann. Auch im Sinne der Testbarkeit allgemein ist es immer besser, nicht zu viel an einer Stelle zu machen.
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Dienstag 24. Juni 2014, 15:03
von Nobuddy
Hallo Hyperion,
ja bei den Einrückungsebenen, bin ich wohl Weltmeister.
Da liegt noch einige Arbeit vor mir, um dies zu verbessern.
Danke auch für den Tip mit 'Unit-Testing', wo ich hier
http://openbook.galileocomputing.de/pyt ... 21_005.htm fündig geworden bin.
Werde mich auch damit auseinandersetzen.
Gegen ein paar kurze Beispiele, zu Deinen Empfehlungen, ahbe ich nichts einzuwenden!

Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Dienstag 24. Juni 2014, 15:29
von Hyperion
Nobuddy hat geschrieben:
ja bei den Einrückungsebenen, bin ich wohl Weltmeister.
Da liegt noch einige Arbeit vor mir, um dies zu verbessern.
Ja, das denke ich auch
Das Buch an sich wird hier ja - wie Du sicherlich weißt - nicht empfohlen. Ich habe den Abschnitt übers Unit-Testing nur kurz überflogen, fand den aber an sich nicht so gut. Das Konzept als solches inkl. der Motivation "wozu das ganze" ist schlicht zu knapp geraten.
Zum Thema Beispiele: Das ist an sich schwer darzustellen, wenn man keinen sinnvollen Code hat! Schau Dir einfach hier im Forum den ein oder anderen Code an, insbesondere natürlich die, die von Regulars überarbeitet werden. BlackJack ist hier eine exzellente Quelle, wenn es um die schöne und sich gut anfühlende Zerlegung von ehemals klumpigen Programmen in hübsche, kompakte und wunderbar lesebar Codes geht. Also, wenn er Code überarbeitet und postet, schau Dir an, wie er den alten aufteilt und überarbeitet

Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Dienstag 24. Juni 2014, 15:31
von Schorlem
So, hab grade auch bemerkt, dass du überhaupt keine anderen Eingaben außer denen in der Liste zulassen wolltest. Ja, lesen sollte man können. Dabei hat Tkinter die nette .validate-Funktion (mehr Infos
hier).
Damit wird der Code 'n bisschen übersichtlicher und behält die Funktionalität. Durch das Validaten der Eingabe gibt es nicht diese seltsamen, nur kurz aufploppenden Zeichen. Außerdem werden Eingaben ignoriert, die nicht mehr in der Liste vorkämen.
Abschließend denke ich, dass die Lösung doch ein wenig eleganter ist (obwohl sie wahrscheinlich gleich sofort von allen auseinandergenommen werden wird)
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x
import tkinter as tk
from tkinter import ttk
class Ttest:
def __init__(self):
self.user_input = ""
self.L = ['aaaaaa', 'abbbbb', 'aaabbb', 'acabbb', 'bbbbbb', 'cccccc',
'vvvvvv', 'wwwwww', 'xxxxxx', 'yyyyyy', 'yywyyy', 'zzzzzz']
self.my_win = tk.Tk()
regValidate = (self.my_win.register(self.validate))
self.frame = tk.Frame(self.my_win)
self.frame.pack(side='top', expand=True)
self.combo = ttk.Combobox(validate="key",
validatecommand=regValidate)
self.combo.bind('<Return>', self.output)
self.combo.bind('<KeyPress>', self.check)
self.combo.focus_set()
self.combo['values'] = self.L
self.combo.pack()
def check(self, event):
if event.keysym == "BackSpace":
self.user_input = self.user_input[:-1]
elif event.char.strip() == '':
return
else:
self.user_input = self.user_input + event.keysym
def output(self, event):
self.combo.set(self.combo.get())
print('Result: ', self.combo.get())
def validate(self):
for item in self.L:
if item.startswith(self.user_input):
self.combo.set(self.user_input + item[len(self.user_input):])
return True
self.user_input = self.user_input[:-1]
return False
def _main():
_ = Ttest()
if __name__ == "__main__":
_main()
Verbesserungsvorschläge weiterhin gern gesehen
(Auch Hinweise darauf, ob ich früher vorhandene Funktionalität ignoriert habe)
Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Dienstag 24. Juni 2014, 16:01
von Nobuddy
Hallo Schorlem,
Die Liste, ist nur zum Testen.
Bei späterer Verwendung, wird die Liste an das Modul übergeben.
Werde mich auch mit der validate-Funktion auseinandersetzen.
Danke für Deinen Input, werde mir das genau anschauen und testen.

Re: ComboBox - Aktualisierung im Ausgabefeld
Verfasst: Dienstag 24. Juni 2014, 17:12
von Nobuddy
Hallo Schorlem,
bei mir funktioniert Dein Vorschlag nicht.
Cursor-Position bleibt immer am Anfang und reagiert nicht auf weitere Tasteneingaben.
Das Anfangsergebnis lässt sich nicht mehr beeinflussen.
Versuche es mal mit meinen Code, damit Du verstehst, was ich meine.