GUI mit TkTable

Fragen zu Tkinter.
Antworten
fbuchinger
User
Beiträge: 29
Registriert: Donnerstag 7. September 2006, 21:30

Hallo,

ich möchte eine einfache GUI für mein Dateiumbenennungsprogramm stricken, das die vorgegebenen Quelldateinamen (nicht änderbar) und die änderbaren Umbenennungsvorschläge in 2 Spalten nebeneinander anzeigt.

Da es sich um ca. 80-100 Einträge handelt, wollte ich mir hierfür eine GUI mit TkTable bauen, damit man rasch mit den Cursortasten zwischen den Vorschlägen navigieren kann.

Weitere Anforderungen:
- Die Tabelle sollte in einem Fenster mit Scrollleiste integriert sein, 30 Einträge sollten gleichzeitig im Fenster erscheinen, darunter OK/Abbrechen-Buttons (permanent sichtbar)
- Beim Klicken auf Ok wird eine Plausibilitätsprüfung für die Dateinamen gestartet (schon vorhanden).

Problem: es gibt kaum Codesamples für Tktable/Python, selbst der Wrapper scheint buggy zu sein. Wenn es also brauchbare Codesnippets gibt, die in diese Richtung gehen... nur her damit!

Natürlich bin ich auch für Alternativvorschläge dankbar.
Benutzeravatar
Mr_Snede
User
Beiträge: 387
Registriert: Sonntag 8. Februar 2004, 16:02
Wohnort: D-Dorf, Bo

So auf Anhieb finde ich nur: http://tkinter.unpythonic.net/wiki/TkTable
Aber vertrauend erweckend sieht das nicht aus.

Ich habe mal für meine Kontoverwaltung sowas hier zusammengebastelt:
http://www.python-forum.de/post-32776.html#32776
fbuchinger
User
Beiträge: 29
Registriert: Donnerstag 7. September 2006, 21:30

aber wie könnte ich dein Beispiel am elegantesten so abändern, dass die Umbennennungsvorschläge in Textfeldern ausgegeben werden (= vom Benutzer editierbar sind)?

Danke,

Franz
Benutzeravatar
Mr_Snede
User
Beiträge: 387
Registriert: Sonntag 8. Februar 2004, 16:02
Wohnort: D-Dorf, Bo

Da die Multilistbox aus mehreren Listboxen zusammengabaut wird, sollte es möglich sein ein Tasttaturkürzel oder eine Maustaste an einzelne Felder zu binden. Um dann ein Dialogfenster aufzurufen.

Aber um ehrlich zu sein, mal eben könnte ich das nicht.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

fbuchinger hat geschrieben:aber wie könnte ich dein Beispiel am elegantesten so abändern, dass die Umbennennungsvorschläge in Textfeldern ausgegeben werden (= vom Benutzer editierbar sind)?
Hallo Franz,

ich finde die MultiListbox von M.Snede einfach klasse! Als Hommage habe ich mal die Funktionalität zum Ändern der Spaltenwerte per Rechtsklick (auf einen MARKIERTEN!! Eintrag) eingefügt, hier die EditableMultiListbox:

Code: Alles auswählen

#http://tkinter.unpythonic.net/wiki/SortableTable
#based on http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52266 by Brent Burley's excellent widget,
#then copy over the following methods to make it sortable by clicking on column header
from Tkinter import *

class EditableMultiListbox(Frame):
   def __init__(self, master, lists):
      Frame.__init__(self, master)
      self.lists = []
      self.colmapping={}
      self.origData = None

      m = PanedWindow(self)#(orient=VERTICAL)
      m.config(handlesize=0, sashrelief=SUNKEN, borderwidth=0)
      m.pack(side=LEFT,fill=BOTH, expand=1)

      for l,w in lists:
         frame = Frame(m)
         frame.config(bg="red", padx=5, pady=5, relief=RAISED, bd=4)
         frame.pack(side=LEFT, expand=YES, fill=BOTH)

         b = Button(frame, text=l, borderwidth=1, relief=RAISED)
         b.pack(fill=X)
         b.bind('<Button-1>', self._sort)
         self.colmapping[b]=(len(self.lists),1)
         lb = Listbox(frame, width=w, borderwidth=4, selectborderwidth=0,
                        relief=RAISED, exportselection=FALSE)
         lb.pack(expand=YES, fill=BOTH)
         self.lists.append(lb)

         m.add(frame)

         lb.bind('<B1-Motion>', lambda e, s=self: s._select(e.y))
         lb.bind('<Button-1>', lambda e, s=self: s._select(e.y))
         lb.bind('<Leave>', lambda e: 'break')
         lb.bind('<B2-Motion>', lambda e, s=self: s._b2motion(e.x, e.y))
         lb.bind('<Button-3>', self._modify)
         lb.bind('<Button-2>', lambda e, s=self: s._button2(e.x, e.y))
         lb.bind('<Button-4>', lambda e, s=self: s._scroll(SCROLL, -1, UNITS))
         lb.bind('<Button-5>', lambda e, s=self: s._scroll(SCROLL, 1, UNITS))
      frame = Frame(self)
      frame.config(bg="green", padx=5, pady=5, relief=RAISED, bd=4)
      frame.pack(side=LEFT, fill=Y)

      b = Button(frame, text="", borderwidth=1, relief=RAISED).pack(fill=X)
     
      sb = Scrollbar(frame, orient=VERTICAL, command=self._scroll)
      sb.pack(expand=YES, fill=Y)
      self.lists[0]['yscrollcommand']=sb.set

      sb.bind('<Button-4>', lambda e, s=self: s._scroll(SCROLL, -1, UNITS))
      sb.bind('<Button-5>', lambda e, s=self: s._scroll(SCROLL, 1, UNITS))

   def _modify(self, e):
      import tkSimpleDialog
      
      #  get current selection line, abort if no line is selected
      i = e.widget.curselection()
      if not i: return

      #  ask for new String      
      Eingabe = tkSimpleDialog.askstring("Neuer Wert", "Eingabe:")

      #  insert new String and update selection      
      e.widget.delete(i)
      e.widget.insert(i, Eingabe)
      self.selection_set(i)

   def _sort(self, e):
      # get the listbox to sort by (mapped by the header button)
      b=e.widget
      col, direction = self.colmapping[b]

      # get the entire table data into mem
      tableData = self.get(0,END)
      if self.origData == None:
         import copy
         self.origData = copy.deepcopy(tableData)

      rowcount = len(tableData)

      #remove old sort indicators if it exists
      for btn in self.colmapping.keys():
         lab = btn.cget('text')
         if lab[0]=='[': btn.config(text=lab[4:])

      btnLabel = b.cget('text')
      #sort data based on direction
      if direction==0:
         tableData = self.origData
      else:
         if direction==1: b.config(text='[+] ' + btnLabel)
         else: b.config(text='[-] ' + btnLabel)
         # sort by col
         def colsort(x, y, mycol=col, direction=direction):
            return direction*cmp(x[mycol], y[mycol])

         tableData.sort(colsort)

      #clear widget
      self.delete(0,END)

      # refill widget
      for row in range(rowcount):
         self.insert(END, tableData[row])

      # toggle direction flag
      if(direction==1): direction=-1
      else: direction += 1
      self.colmapping[b] = (col, direction)

   def _select(self, y):
      row = self.lists[0].nearest(y)
      self.selection_clear(0, END)
      self.selection_set(row)
      return 'break'

   def _button2(self, x, y):
      for l in self.lists: l.scan_mark(x, y)
      return 'break'

   def _b2motion(self, x, y):
      for l in self.lists: l.scan_dragto(x, y)
      return 'break'

   def _scroll(self, *args):
      for l in self.lists:
         apply(l.yview, args)
      return 'break'

   def curselection(self):
      return self.lists[0].curselection()

   def delete(self, first, last=None):
      for l in self.lists:
         l.delete(first, last)

   def get(self, first, last=None):
      result = []
      for l in self.lists:
          result.append(l.get(first,last))
      if last: return apply(map, [None] + result)
      return result

   def index(self, index):
      self.lists[0].index(index)

   def insert(self, index, *elements):
      for e in elements:
         i = 0
         for l in self.lists:
            l.insert(index, e[i])
            i = i + 1

   def size(self):
      return self.lists[0].size()

   def see(self, index):
      for l in self.lists:
         l.see(index)

   def selection_anchor(self, index):
      for l in self.lists:
         l.selection_anchor(index)

   def selection_clear(self, first, last=None):
      for l in self.lists:
         l.selection_clear(first, last)

   def selection_includes(self, index):
      return self.lists[0].selection_includes(index)

   def selection_set(self, first, last=None):
      for l in self.lists:
         l.selection_set(first, last)

if __name__ == '__main__':

   tk = Tk()
   Label(tk, text='MultiListbox').pack()
   mlb = EditableMultiListbox(tk, (('Head1', 5), ('Head2', 10), ('Head3', 20)))
   for zeile in xrange(20):
      mlb.insert(END, (zeile,  'john',  'Alex Kellman'))
      mlb.pack(expand=YES,fill=BOTH)
   tk.mainloop()
Wenn ich mit dem Copyright mal wieder was falsch gemacht habe, bitte korrigieren.

Grüße,
der Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
Mr_Snede
User
Beiträge: 387
Registriert: Sonntag 8. Februar 2004, 16:02
Wohnort: D-Dorf, Bo

Super Sache! So macht das Laune :-)

Der Code hätte aber besser in dem Thread [1] gepasst, in dem ich dieses Widget vorgestellt und erweitert habe.
Vielleicht mag ein Mod den Post verschieben.

[1] http://www.python-forum.de/post-32776.html#32776
Antworten