Suchfunktion in Listbox???

Fragen zu Tkinter.
Antworten
PIERRESTEEG
User
Beiträge: 14
Registriert: Freitag 14. März 2008, 12:21

Hallo,
ich hab mal wieder ein problem :lol:
Ich habe eine Listbox und ein Entryfield.
Das Entryfield (sollte!!!) dafür zuständig sein, einträge aus der listbox herauszusuchen. Wenn ich z.b. gerade im entry field eine 3 eingebe, soll er mir nur noch die Einträge zeigen, die mit einer 3 anfangen...
Ist das irgendwie möglich? Brauche dringend Hilfe!
Danke schonmal im vorraus :D

P.S.: ist es möglich, mit einer scrollbar 3 listboxes zu bewegen???
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Damit ich es genau verstehe:
Soll sich der dargestellte Inhalt der Listbox SOFORT ändern, sobald du eine "3" eingibst, oder erst, nachdem du die Eingabe von "3" durch <Enter> oder einen Buttonclick bestätigt hast?
PIERRESTEEG
User
Beiträge: 14
Registriert: Freitag 14. März 2008, 12:21

Wenn möglich, sofort
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Vielleicht so?

Code: Alles auswählen

import Tkinter as tk
from random import randint

def filterlbox(e):
    for k in xrange(lbox.size()-1,-1,-1):
        if not str(lbox.get(str(k))).startswith(e.char):
            lbox.delete(str(k))

root = tk.Tk()
lbox = tk.Listbox(root,height=30)
lbox.pack()
searchfield = tk.Entry(root)
searchfield.pack()
searchfield.focus_set()
searchfield.bind("<Any-KeyPress>",filterlbox)
for k in xrange(30):
    lbox.insert("end",randint(100000,999999))
root.mainloop()
Das macht zunächst nur das, was du wolltest, verhält sich noch nicht schön, wenn man eine zweite Ziffer eingibt, weil nicht klar ist, wie das Programm darauf reagieren soll:
Soll immer nur eine Ziffer eingegeben werden, könnte man das Entry-Widget danach z.B. auf DISABLED setzen, oder den Inhalt löschen o.ä.
Soll auch noch eine zweite, dritte etc. Ziffer eingegeben werden zur Verfeinerung, dann müsste man die Bedingung für das Entfernen etwas anders realisieren.
PIERRESTEEG
User
Beiträge: 14
Registriert: Freitag 14. März 2008, 12:21

Vielen Dank, das war wirklich hilfreich :)
Aber ich muss es noch irgendwie hinkriegen, dass er auch weitersucht, wenn ich eine weitere Zahl eingebe... :?:

Ich würde mich natürlich immer noch sehr über Beiträge freuen ^^´
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Neuer Versuch:

Code: Alles auswählen

import Tkinter as tk
import random

def myfilter(e):
    filterstr = filterfield.get()
    if e.char in "0123456789": filterstr += e.char # VORSICHT!
    for box in lboxes:
        filteredcontent = [item for item in box.content if item.startswith(filterstr)]
        box.delete(0,box.size())
        box.insert("end",*filteredcontent)

def scroll_lboxes(*args):
    for box in lboxes: box.yview(*args)

root = tk.Tk()
lboxframe = tk.Frame(root,padx=10,pady=10)
lboxframe.pack()
scroller = tk.Scrollbar(lboxframe,command=scroll_lboxes)
scroller.pack(side=tk.RIGHT,fill=tk.Y)

lboxes = list()
for n in range(3):
    box = tk.Listbox(lboxframe,width=12,height=30,yscrollcommand=scroller.set)
    box.pack(side=tk.LEFT)
    box.content = [str(random.randint(10000000,99999999)) for k in xrange(100)]
    box.insert("end",*box.content)
    lboxes.append(box)

filterfield = tk.Entry(root)
filterfield.pack()
filterfield.focus_set()
filterfield.bind("<Any-KeyPress>",myfilter)
root.mainloop()
Das könnte ein Anfang sein, hat aber noch Schwachstellen:
Hauptproblem ist, dass bei der Auswertung des Key-Events der Inhalt des Entry-Feldes noch nicht aktualisiert ist. Das habe ich nicht hinbekommen.
Ich habe es mit einem idle_update() probiert, mal eine kleine Wartezeit eingebaut, hat aber alles nicht geholfen.

Vielleicht gibt es jemanden mit mehr Erfahrung, der hierfür eine Lösung weiß.

Mögliche Notlösungen: Nach Eingabe einer Ziffer drückt man einmal <Enter>.
Dann muss Zeile 6 weg und eigentlich funktioniert es dann sauber.

Oder: Man lässt Zeile 6, müsste dann aber noch weitere Auswertungsarbeit leisten, um beim Einsatz der Cursortasten kombiniert mit <Del> oder <Backspace> noch die richtige Ziffernkombination zu erhalten.
Solange man nur eine Ziffer nach der anderen eingibt und ggf. mittels <Backspace> - mit einer Ziffer Verzögerung - wieder von rechts löscht, geht es auch so.

Nicht so angenehm zu bedienen, aber dafür immer mit korrekter Filterung wäre es, wenn die Filterung erst nach einem Buttonclick durchgeführt würde (der Button fehlt noch ...).

Entscheidend ist hier m.E. auch die Frage, ob es nur für den Eigenbrauch gedacht ist, oder ob auch andere mit dem Programm arbeiten sollen. Im letzteren Fall würde ich es, so wie es jetzt ist, NICHT lassen.
Zuletzt geändert von numerix am Mittwoch 19. März 2008, 16:32, insgesamt 1-mal geändert.
PIERRESTEEG
User
Beiträge: 14
Registriert: Freitag 14. März 2008, 12:21

wow, vielen dank! :D
Ich habe gerade ein 3-tägiges Praktikum und man hat mir halt diese Aufgabe gestellt. Hab schon das ganze web abgegrast, werd aber nicht schlauer ^^´.
Es gibt noch viele andere Dinge die ich tun muss, aber ich bin froh, wenn ich dass schonmal (irgendwie) weiß.
Jetzt schau ich aber erstmal, wie gut ich mit den anderen Sachen zurecht komme.
Vielen dank nochmal
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

pütone hat geschrieben:Hauptproblem ist, dass bei der Auswertung des Key-Events der Inhalt des Entry-Feldes noch nicht aktualisiert ist. Das habe ich nicht hinbekommen.
Man müsste es an ein Event des Entry-Feldes knüpfen.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

mkallas hat geschrieben:Man müsste es an ein Event des Entry-Feldes knüpfen.
Habe ich nicht genau das in Zeile 32 getan?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Eine Funktion ``filter`` zu nennen ist keine gute Idee, weil sie ``filter`` aus den Builtins überdeckt...
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Leonidas hat geschrieben:Eine Funktion ``filter`` zu nennen ist keine gute Idee, weil sie ``filter`` aus den Builtins überdeckt...
Als ich den Code entwickelt habe, hatte ich schon kein gutes Gefühl dabei, weil mir das bewusst war.
Da das builtin filter() hier nicht benötigt wird, hatte ich es stehen lassen ... aber gut ist das nicht. Es ist schlecht. Hab's geändert.
PIERRESTEEG
User
Beiträge: 14
Registriert: Freitag 14. März 2008, 12:21

Wie mach ich das denn nun, dass er mir NUR eine List durchsucht???
Kriegs irgendwie nicht hin :cry: Hat jemand eine Verbesserung?
Sry, bin n ziemlicher Anfänger :oops:


def filter(e):
temp_list = list(listbox.get(0, END))
filterstr = filterfield.get()
if e.char in "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ": filterstr += e.char # VORSICHT!
#for box in lboxes:
filteredcontent = [item for item in temp_list if item.startswith(filterstr)]
listbox.delete(0,listbox.size())
listbox.insert("end",*filteredcontent)

filterfield = Entry(root)
filterfield.place(x=762,y=237)
filterfield.focus_set()
filterfield.bind("<Any-KeyPress>",filter)

Weiß leider nicht, wie man hier korrekt codeschnippsel darstellt, daher falsche einrückungen
BlackJack

Für Quelltext gibt's code-Tags. Entweder per Hand eintippen, oder über den "Python"-Knopf über dem Textfeld beim Bearbeiten, oder die Drop-Downbox mit den verschiedenen Programmiersprachen und dem "Highlighted Code"-Knopf daneben.
PIERRESTEEG
User
Beiträge: 14
Registriert: Freitag 14. März 2008, 12:21

Das mit einer Liste hat sich jetzt geklärt :)
Hab doch mal was alleine hingekriegt ^^
bpkri
User
Beiträge: 1
Registriert: Sonntag 17. August 2008, 21:29

so, ich hatte grade auch das Problem,da ss ich eine Filterbase Listbox haben wollte.

Hier ist meine Llösung:

Code: Alles auswählen

import Tkinter as tk 
import random 

def myfilter(e):
    filterfield.after_idle(processentry)

def processentry():
    filterstr = filterfield.get()
    for box in lboxes: 
        filteredcontent = [item for item in box.content if filterstr in item] 
        box.delete(0,box.size()) 
        box.insert("end",*filteredcontent)


def scroll_lboxes(*args): 
    for box in lboxes: box.yview(*args) 

root = tk.Tk() 
lboxframe = tk.Frame(root,padx=10,pady=10) 
lboxframe.pack() 
scroller = tk.Scrollbar(lboxframe,command=scroll_lboxes) 
scroller.pack(side=tk.RIGHT,fill=tk.Y) 

lboxes = list() 
for n in range(1): 
    box = tk.Listbox(lboxframe,width=12,height=30,yscrollcommand=scroller.set,selectmode="extended") 
    box.pack(side=tk.LEFT) 
    box.content = [str(random.randint(10000000,99999999)) for k in xrange(100)] 
    box.insert("end",*box.content) 
    lboxes.append(box) 

filterfield = tk.Entry(root) 
filterfield.pack() 
#filterfield.focus_set() 
filterfield.bind("<Any-KeyPress>",myfilter)

root.mainloop()
Der Trick ist eigentlich nur, dass ich nicht direkt dne Filter Processe, sondern erst mal nur after_idle aufrufe. Dann kann man sich das rumgehampele mit den chars sparen. Und es funktioniert dann auch für verschiedenste Eingaben, macht beim Backspace keine Probleme, usw.

Problematisch ist jetzt noch: Die Textbox utner der Liste schnappt sich ja den even <Any-KeyPress> - das ist nicht ideal, weil die Listbox an sich auch bestimmte Tasten entgegennimmt. Besser wäre es, wenn sich die ListBox nur den Event shcnappt, wenn sie den Fokus hat (Reinklicken oder mit Tab rein-oder rausgehen) - das hab ich aber noch nicht so richtig hinbekommen.

Ach - das kann man sicher auch noch weiter verkürzen. :)
Antworten