Frame aus Frame löschen mit Objekt aus Liste

Fragen zu Tkinter.
Benutzeravatar
DeKugelschieber
User
Beiträge: 82
Registriert: Sonntag 28. Februar 2010, 12:23
Kontaktdaten:

Hallo,

ich hab das Problem dass ich mehrere Frame Objekte (inklusive innere Labels) in einer Liste gespeichert habe. Jetzt möchte ich jedes Frame in der Liste löschen, so das es nicht mehr angezeigt wird. Jetzt weiß ich nicht genau wie ich auf die Objekte zugreifen kann.

Hier der Code:

http://paste.pocoo.org/show/187519/
MfG DeKugelschieber
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo DeKugelschieber !

Code: Alles auswählen

#! /usr/bin/env python
# -*- coding: utf-8

import Tkinter as tk

class Grider(object):
    def __init__(self, root):
        self.labels_grid = True
        labels = ("__________Label 1__________", 
                  "__________Label 2__________", 
                  "__________Label 3__________")
        self.labels = list()
        self.button = tk.Button(root, text="Grid Forget", width = 17,
            command=self.grid_labels)
        self.button.grid(row=0, column = 0)
        for row, label_text in enumerate(labels):
            label = tk.Label(root, text = label_text)
            label.grid(row = row+1, column = 0)
            self.labels.append(label)
        
            
    def grid_labels(self):
        if self.labels_grid:
            self.text = "Grid"
            self.labels_grid = False
            for label in self.labels:
                label.grid_forget()
        else:
            self.text = "Grid Forget"
            self.labels_grid = True
            for label in self.labels:
                label.grid()
            
        self.button.config(text = self.text)
            


def main():
    root = tk.Tk()
    gui = Grider(root)
    root.title("Grider")
    root.resizable(0, 0)
    root.mainloop()
     
if __name__ == "__main__": main()
Gruß Frank
BlackJack

Einfach über die Liste iterieren und die `destroy()`-Methode aufrufen!?

Ich bin mir übrigens ziemlich sicher dass die Namen auf Klassenebene da nicht stehen sollten. Die gelten dann für *alle* Exemplare und nicht für jedes einzeln.

Ausserdem ist das `x` in `show_items()` überflüssig. Einfach den `Frame` an einen lokalen Namen binden und den benutzen statt immer wieder auf das letzte Element der Liste mittels `x` zuzugreifen.

`tmp` ist auch unschön. Dafür gibt's das automatische entpacken von Sequenzen. Das geht sogar direkt in der Schleife: ``for text, image in self.items:``
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo DeKugelschieber

Habe dein Skript ein bisschen frisiert.
Für das Einrücken würde ich Tabs mit 4 Leerzeichen verwenden.
Den Name 'list' nicht zum speichern von Objekten verwenden, 'list' ist ein reserviertes Wort. Habe es in 'list_icon' abgeändert.
Löschen würde ich wie es BlackJack vorschlägt. Nach dem Löschen bleibt die Referenz des gelöschten Frame-Objektes in der Lsite bestehen. Ich würde sie mit NONE überschreiben.

Hier dein modifiziertes Skript:
http://paste.pocoo.org/show/187586/

Gruß wuf :wink:
Take it easy Mates!
Benutzeravatar
DeKugelschieber
User
Beiträge: 82
Registriert: Sonntag 28. Februar 2010, 12:23
Kontaktdaten:

Vielen Dank für eure Hilfe! Ihr habt mir mal wieder ein riesen Stück weiter geholfen :D. Ich hatte da gerade eine Denkblockade, aber jetzt schaff ichs sicher. Ich arbeite das dann mal auf.

@wuf: Ich benutze 4 Leerzeichen (ist ja auch üblich) aber Paste macht da irgendeinen mist mit.
MfG DeKugelschieber
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo kaytec

Danke noch für dein interessantes Code-Snippet 'The Grider'

Hier habe ich dein Skript noch für den 'Packer' umgeschrieben:
The Packer

Gruß wuf :wink:
Take it easy Mates!
Benutzeravatar
DeKugelschieber
User
Beiträge: 82
Registriert: Sonntag 28. Februar 2010, 12:23
Kontaktdaten:

Sooooooooooooooo

Hier mal das fertige Ergebnis, gefällt mir noch besser als der Ansatz von wuf. Aber Eigenlob stinkt.

http://paste.pocoo.org/show/187874/

Die Grafiken müsst ihr selber stellen, meine sind jetzt 16x16px, sonst müsst ihr halt set_icon_size() benutzen.
MfG DeKugelschieber
BlackJack

@DeKugelschieber: Du hast da immer noch Klassenattribute von denen sicher nicht alle dort stehen sollten. Erstell mal *zwei* von diesen Objekten, dann siehst Du was ich meine.

Bei `insert()` würde ich `None` statt `NONE` nehmen, oder dann auch noch ``'none'`` im Quelltext. Das finde ich bei `delete()` auch merkwürdig. Warum wird das da nicht ordentlich aus der Liste entfernt? Dabei fällt mir gerade der Sternchenimport von `tkinter` auf.

Die ganze Indirektion über einen Index und die `x` in den Methoden erscheinen mir auch überflüssig.

In `show_items()` ist "copy'n'paste"-Code der nicht sein müsste. Alles was die beiden Zweige am Ende gemeinsam haben kann auch *einmal* nach dem ``if``/``else`` stehen.

`callback()` erscheint mir wieder unnötig kompliziert. In `self.selected` könnte man einfach an `None` oder das entsprechende `Frame`-Objekt binden statt einen Index zu verwenden. Und beim `callback()` sollte man nicht erst die GUI-Widgets durchprobieren müssen, sondern das `Frame`-Exemplar gleich beim Aufsetzen der Rückruffunktion mit `functools.partial` festlegen. Dann wird's direkt übergeben und muss nicht erst mühsam ermittelt werden. Eventuell würde es sich auch anbieten so einen `Frame` mit den drei enthaltenen Widgets in eine eigene Klasse zu stecken.
Benutzeravatar
DeKugelschieber
User
Beiträge: 82
Registriert: Sonntag 28. Februar 2010, 12:23
Kontaktdaten:

@ BlackJack: Ich werd mal versuchen alles umzusetzten, aber wie soll ich bei dem "x" sonst an den Index kommen? Ich hab dazu nichts gefunden.
MfG DeKugelschieber
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo DeKugelschieber

Sich selber auf die Schultern kloppfen ist ein wichtig Motivator um die nächsten Hürden anzugehen Mir gefällt dein Skript erst wenn du es endlich schaffst das Einrücken mit 'Tabs made out of four Spaces' zu bewerkstelligen. Wenn dein Skript läuft zeigt sich ein augenschädigendes Flackern der Icons. Muss das so sein?

Gruss wuf :wink:
Take it easy Mates!
Benutzeravatar
DeKugelschieber
User
Beiträge: 82
Registriert: Sonntag 28. Februar 2010, 12:23
Kontaktdaten:

Das sind 4 Leerzeichen:

Bild

@ BlackJack: Das Element wird ordentlich enfehrnt, aber erst später, da ich erst "pack_forget()" anwende. Das Element wird sozusagen geflaggt:

Code: Alles auswählen

# Alte Labels loeschen
		if self.first_watch:
			for tmp in self.items:
				if tmp != 'none':
					tmp[0].pack_forget()
				else:
					self.items.remove(tmp)				
		self.first_watch = True
Und ich finde dass das in einer Klasse schöner ist, da muss man keine neuen Objekte erzeugen, das regelt die Klasse selber. Wie bekomme ich denn bei dem event die id des widgets?
Zuletzt geändert von DeKugelschieber am Mittwoch 10. März 2010, 17:01, insgesamt 1-mal geändert.
MfG DeKugelschieber
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

....... Genau dies würde mir gefallen. Ich arbeite hier mit KWrite under SuSE11.0. Habe keine Probleme beim transferieren nach http://paste.pocoo.org/

Gruß wuf :wink:
Take it easy Mates!
Benutzeravatar
DeKugelschieber
User
Beiträge: 82
Registriert: Sonntag 28. Februar 2010, 12:23
Kontaktdaten:

ist das nicht egal so lange es lesbar ist? sonst schreib ich mir mal selber einen highlighter in php^^

So hier überarbeitet:

http://paste.pocoo.org/show/187960/

Wie ich die "x" wegbekomme und das mit dem event geht versteh ich noch nicht so ganz. Wie findet ihr generell eigentlich meine Idee? Ich kann damit jetzt arbeitet wie mit einer Listbox, nur noch mit Icons.

EDIT:

Ok es lässt sich super einbauen, aber wie löse ich es jetzt das ich eine Scrollbar habe? Frame kann keine bekommen. Ich habs so probiert:

Code: Alles auswählen

		scroll_canvas = Canvas(self.window, width = 200)
		scroll = Scrollbar()
		scroll.config(command = scroll_canvas.yview)
		scroll_canvas.config(yscrollcommand = scroll.set, width = 30)		
		self.list = IconList(scroll_canvas)
		self.list.pack(side = TOP, fill = BOTH)
		scroll_canvas.pack(side = LEFT, fill = Y)
		scroll.pack(side = LEFT, fill = Y)
MfG DeKugelschieber
BlackJack

@DeKugelschieber: `items` ist immer noch ein *Klassenattribut*. Damit ist die Klasse IMHO kaputt. Man kann nicht mehrere von diesen `IconList`\s in einem Programm verwenden.

Dass das Element zum Löschen nur markiert wird, ist mir klar, nur nicht *warum*. Das könnte man doch alles sofort erledigen. Insbesondere ist Dein Löschen aus der Liste auch kaputt. Wenn Du etwas aus einer Liste löschst während Du darüber iterierst verschieben sich alle Elemente um eins "nach vorne" und im nächsten Schleifendurchlauf wird dadurch ein Element übersprungen:

Code: Alles auswählen

In [33]: a = range(10)

In [34]: for b in a:
   ....:     a.remove(b)
   ....:

In [35]: a
Out[35]: [1, 3, 5, 7, 9]
Die Items nicht in eine eigene Klasse zu kapseln ist für Dich vielleicht schöner, aber es ist keine ordentliche objektorientierte Programmierung. Und neue Objekte erzeugst Du auch ohne eigene Klasse. Du steckst die Einzelteile ja in neue Listen.

An dem Bildschirmphoto sehe ich übrigens nicht, ob das ein Tab oder vier Leerzeichen pro Ebene sind. Und wenn man Deine Quelltexte hier im Forum oder beim Paste-Dienst anschaut, sieht's eher nach Tabs aus.

Das `x` kannst Du wegbekommen, indem Du nicht mit Indexen, sondern direkt mit den Objekten in der Liste arbeitest.

Kann Deine Klasse übrigens damit umgehen, wenn sie keine Items enthält!?
Benutzeravatar
DeKugelschieber
User
Beiträge: 82
Registriert: Sonntag 28. Februar 2010, 12:23
Kontaktdaten:

DeKugelschieber: `items` ist immer noch ein *Klassenattribut*. Damit ist die Klasse IMHO kaputt. Man kann nicht mehrere von diesen `IconList`\s in einem Programm verwenden.
Check ich nicht, was heißt das jetzt? Es geht nicht mit zweien, aber was muss ich ändern (gewohnheit aus anderen Programmiersprachen)?
Dass das Element zum Löschen nur markiert wird, ist mir klar, nur nicht *warum*. Das könnte man doch alles sofort erledigen. Insbesondere ist Dein Löschen aus der Liste auch kaputt. Wenn Du etwas aus einer Liste löschst während Du darüber iterierst verschieben sich alle Elemente um eins "nach vorne" und im nächsten Schleifendurchlauf wird dadurch ein Element übersprungen:
Hab ich geändert, ich wusste erst nicht das remove ein Objekt und keinen Index will, ich denk dabei immer an arrays, aber da muss ich wohl Dictonaries nehmen (blöde Namen für Sachen die es schon gibt).
An dem Bildschirmphoto sehe ich übrigens nicht, ob das ein Tab oder vier Leerzeichen pro Ebene sind. Und wenn man Deine Quelltexte hier im Forum oder beim Paste-Dienst anschaut, sieht's eher nach Tabs aus.
Glaubt ihr mir nicht?! Mein Gott das sind 4 Leerzeichen!
Das `x` kannst Du wegbekommen, indem Du nicht mit Indexen, sondern direkt mit den Objekten in der Liste arbeitest.
Nö dann bleich ich lieber beim x, mit Indexen kann ich später mehr anfangen und es soll ja wie bei der Listbox sein.
Kann Deine Klasse übrigens damit umgehen, wenn sie keine Items enthält!?
Klar! Dann wird ein Leeres Frame angezeigt, was auch sonst.

----------------------------------------------------------------------------------

Bevor ich jetzt sonst was verbessere, wie bekomme ich die Scrollbar hin?
Hier mein Ansatz (schon verbessert, funktioniert trotzdem nicht):

Code: Alles auswählen

		# !!!!!!!!!!!!!!!!!!!!!!!!!!!! FUNKTIONIERT NOCH NICHT !!!!!!!!!!!!!!!!!!!!!!!!!!!!
		scroll_canvas = Canvas(self.window, width = 200)
		scroll_canvas.pack(side = LEFT, fill = X)
		scroll_y = Scrollbar(self.window)
		scroll_y.config(command = scroll_canvas.yview)
		scroll_y.pack(side = LEFT, fill = Y)
		scroll_canvas.config(yscrollcommand = scroll_y.set)		
		self.list = IconList(scroll_canvas)
		self.list.pack(side = TOP, fill = BOTH)
MfG DeKugelschieber
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

DeKugelschieber hat geschrieben:
DeKugelschieber: `items` ist immer noch ein *Klassenattribut*. Damit ist die Klasse IMHO kaputt. Man kann nicht mehrere von diesen `IconList`\s in einem Programm verwenden.
Check ich nicht, was heißt das jetzt? Es geht nicht mit zweien, aber was muss ich ändern (gewohnheit aus anderen Programmiersprachen)?
Indem du Items an das Exemplar/das Objekt bindest und nicht die Klasse.
Das Leben ist wie ein Tennisball.
Benutzeravatar
DeKugelschieber
User
Beiträge: 82
Registriert: Sonntag 28. Februar 2010, 12:23
Kontaktdaten:

Das ist doch mal eine Aussage^^ Nee quwatsch, ich habs jetzt verstanden. Funktioniert, ich kann mehrere Listen erzeugen.

Ich habe jetzt konkret noch 2 Probleme:

1. Ich will mehrfachlöschung so wie bei der Listbox, also "delete(4, 9)" z.B.. Dann werden 5 Items von 5-10 gelöscht. Das mit dem itaieren hab ich verstanden, weiß aber trotzdem nicht wie ich das umsetzten soll:

Code: Alles auswählen

	def delete(self, id, end = None):
		if id <= len(self.items)-1:
			if end == None:
				self.items[id][0].pack_forget()
				self.items.remove(self.items[id])
			else:
				for i in range(id, end):
					self.items[i][0].pack_forget()
					self.items.remove(self.items[i])
			self.show_items()
so stell ich mir das vor, funktioniert aber natürlich nicht. Wie soll ich es sonst machen? So funktioniert es auch nicht:

Code: Alles auswählen

	def delete(self, id, end = None):
		if id <= len(self.items)-1:
			if end == None:
				self.items[id][0].pack_forget()
				self.items.remove(self.items[id])
			else:
				for i in range(id, end+1):
						self.items[i][0].pack_forget()
				for i in range(id, end+1):
						self.items.remove(self.items[i])
			self.show_items()
2. Wie bekomme ich eine Scrollbar an ein Canvas oder sonst was in das ich später das Frame mit dem Items packe? Es wird weder ein Balken noch der Rest angezeigt wenn ich mit dem Mausrad scrolle:

Code: Alles auswählen

		scroll_canvas = Canvas(self.window)
		scroll_canvas.pack(side = LEFT, fill = X)
		scroll_y = Scrollbar(self.window)
		scroll_y.config(command = scroll_canvas.yview)
		scroll_y.pack(side = LEFT, fill = Y)
		scroll_canvas.config(yscrollcommand = scroll_y.set)		
		self.list = IconList(scroll_canvas)
		self.list.pack(side = TOP, fill = BOTH)
MfG DeKugelschieber
Benutzeravatar
DeKugelschieber
User
Beiträge: 82
Registriert: Sonntag 28. Februar 2010, 12:23
Kontaktdaten:

So, ist mir jetzt total egal ob ich flaggen darf oder nicht :D. WENN ich flagge und später erst lösche funktioniert es einwandfrei! Warum sollte ich es nicht so machen?

Code: Alles auswählen

	def delete(self, id, end = None):
		if id <= len(self.items)-1:
			if end == None:
				self.items[id][0].pack_forget()
				self.items[i] = None
			else:
				for i in range(id, end+1):
					self.items[i][0].pack_forget()			
					self.items[i] = None
			self.show_items()
			
	def show_items(self):	
		# Labels anzeigen
		x = 0
		for tmp in self.items:
			if tmp == None:
				self.items.remove(tmp)
			else:
				if x == self.selected:
					tmp[0].config(bg = '#3399ff')
					tmp[1].config(bg = '#3399ff', fg = '#ffffff')
					tmp[2].config(bg = '#3399ff')
				tmp[0].pack(side = TOP, fill = BOTH)
				x = x+1
Bleibt nurnoch Frage 2.
MfG DeKugelschieber
BlackJack

@DeKugelschieber: Das `remove()` ein Objekt nimmt steht aber in der Doku. Wenn Du ein Element an einem Index entfernen willst, geht das (etwas gewöhnungsbedürftig) mit der ``del``-Anweisung. Oder mit `pop()` falls man das Element noch verwenden möchte. Das was Du jetzt machst ist jedenfalls ziemlich ungünstig. Du holst Dir das Objekt aus der Liste von einem bekannten Index und `remove()` sucht dann dieses Objekt linear in der Liste -- obwohl Du den Index doch bereits *kennst*.

Das man `id`\s ausserhalb der Länge der Items löschen kann, finde ich unschön. Wenn jemand so etwas versucht macht er einen Fehler, und da sollte vielleicht auch eine Ausnahme bei rumkommen.

Die beiden Fälle würde ich nicht so unterschiedlich behandeln. Man kann den Code auch so schreiben, dass *immer* ein Bereich gelöscht wird. Wenn `end` nicht angegeben wird, dann eben der Bereich von `id` bis `id`+1. Ungetestet:

Code: Alles auswählen

    def delete(self, id, end=None):
        if end is None:
            end = id + 1
        for item in self.items[id:end]:
            item.pack_forget()
        del self.items[id:end]
        self.show_items()
Das mit den vier Leerzeichen glaube ich nicht, weil's hier im Forum und beim Paste definitiv Tabs sind. Und an eine automatische Umwandlung irgendwo auf dem Weg vom Editor in den Browser glaube ich nicht, weil es ja diesen Fall von Leerzeichen und Tabs gemischt gab, wo der Syntaxfehler wegen falscher Einrückung kam.

Wenn Du `x` behalten willst, schau Dir wenigstens `enumerate()` an statt das "per Hand" hochzuzählen.

Ohne Items ist trotzdem bei `selected` der Index 0 vermerkt. Ich dachte das könnte vielleicht irgendwo Probleme geben.

Zum letzten Code: Das funktioniert so nicht zuverlässig. Wenn nach dem entfernten Eintrag der ausgewählte kommt, wird der in der Schleife übersprungen und damit -- zumindest in *dem* Lauf der Methode nicht als ausgewählt eingefärbt.
Benutzeravatar
DeKugelschieber
User
Beiträge: 82
Registriert: Sonntag 28. Februar 2010, 12:23
Kontaktdaten:

Das reicht heute erstmal, ich mach morgen weiter, schonmal vielen Dank, aber geklappt hats noch nicht. Das das Tabs sind ist komisch, der muss das nach dem Update wieder geändert haben, ich habs jetzt wieder auf 4 Leerzeichen gestellt.
MfG DeKugelschieber
Antworten