Tabellengrid funktioniert nicht so wie gewollt

Fragen zu Tkinter.
Antworten
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

Hallo, habe versucht mir ein eigenes Tabellengrid zu erstellen, das Design passt auch, aber
beim Setzen von Zellenwerten wird immer die Zelle in der letzten Zeile gesetzt, dh. die Spalte passt, aber die zeilennummer ist falsch.

Hoffe von Euch siehts jemand schneller als ich. Danke

Hier der Code:

Code: Alles auswählen

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

import os
import sys
import ttk

# Setup the Logging - System:

import logging
Logger = logging.getLogger(__name__)        # so Logger is unique
Logger.setLevel(logging.NOTSET)             # no level at develtime
Logger.addHandler(logging.NullHandler())    # no destination of ...

ENC = "utf-8"

class MyTableError(ValueError): pass
class MyTableOptionError(ValueError): pass 


class Table(object):
    """
    >>> root = ttk.Tkinter.Tk()
    >>> tabelle = Table(root,
    >>>                 3,
    >>>                 4,
    >>>                 width=[5,10,3,12,9,8,10,11, 9, 12],
    >>>                 columnhead=True,
    >>>                 columnheads=["S1", "S2", "S3", "S4"],
    >>>                 rowhead=True,
    >>>                 rowheads=[
    >>>                     "Dataset 1:",
    >>>                     "Dataset 2:",
    >>>                     "Dataset 3:"],
    >>>                 entrybg="#ABC",
    >>>                 labelbg="#234567"
    >>>                 )
    >>> # teste setCell
    >>> zelle = tabelle.setCell(1,2,"ADF")
    >>> 
    >>> root.mainloop()
    
    """
    def __init__(self, parent, rows, columns, **kwargs):
        _c = "<%s>" % (self.__class__.__name__,)
        Logger.debug(_c)
        
        try:
           top = parent.winfo_toplevel()
        except:
            err = "%s need a tkinter object to draw on!" % (_c, )
            Logger.exception(err)
            raise ValueError(err)
        
        try:
            rows = abs(int(rows))
            columns = abs(int(columns))
        except Exception:
            err = "%s rows and columns need integer values or values " % (_c,)
            err += "convertable to integer!"
            Logger.exception(err)
            raise ValueError(err)
        
        if rows == 0:
            err = "%s you set the row counter to 0!" % (_c,)
            Logger.exception(err)
            raise ValueError(err)
        elif columns == 0:
            err = "%s you set the column counter to 0!" % (_c,)
            Logger.exception(err)
            raise ValueError(err)
        
        self.parent = parent
        self.rows = rows + 1        # + column header
        self.columns = columns + 1  # + row header
        self.frm = ttk.Tkinter.Frame(self.parent)
        
        # Datamodel with header
        
        zeile = [None, ]*self.columns
        self._matrix = [zeile,]*self.rows
        
        self.options = {
            "vartype": "s",
            "columnhead": False,
            "columnheads": ["", ] + range(self.columns),
            "rowhead": False,
            "rowheads": ["", ] + range(self.rows),
            "width": [1,] + [10,]*(self.columns-1),
            "entrystyle": "sunken",
            "entrybg": "#FFF",
            "labelstyle": "groove",
            "labelbg": "#AAA"
            }
        self.setOptions(**kwargs)
        
        self.setup()
        self.frm.pack()
        Logger.info("%s done", _c)
    
    def setOptions(self, **kwargs):
        _c = "<%s>" % (self.__class__.__name__,)
        Logger.debug(_c)
        
        def raiseerror(key="", value="", err="option %s has wrong value %s"):
            if key and value:
                txt = err % (key, value)
            elif key:
                txt = err % (key,)
            elif value:
                txt = err % (value,)
            else:
                txt = err
            Logger.exception(txt)
            raise MyTableOptionError(txt)
        
        # check options
        Logger.debug("%s check option %s [value=%s]")
        for k,v in kwargs.items():
            
            Logger.debug("%s set option %s [value=%s]", _c, k, v)
        
            if k.lower() not in self.options.keys():
                raiseerror(k,v)
        
            # pruefen der einzelnen Optionen
            
            k = k.lower()
            if k  == "rowhead" or k == "columnhead":
                try:
                    self.options[k] = bool(kwargs[k])
                except:
                    raiseerror(k,v)
            
            elif k == "columnheads" or k == "rowheads":
                if type(v).__name__ not in ["list", "tuple"]:
                    err = "option %s needs a sequence type  [got %s instead]"
                    raiseerror(k, type(v).__name__, err)
                
                if k == "columnheads":
                    if len(v) < self.columns - 1:
                        err = "%s column header needed! Got only %s"
                        raiseerror(self.columns - 1, len(v), err)
                    
                    self.options[k] = ["",] + v[:self.columns - 1]
                
                elif k == "rowheads":
                    if len(v) < self.rows - 1:
                        err = "%s row header needed! Got only %s"
                        raiseerror(self.rows - 1, len(v), err)
                    
                    v = v[:self.rows - 1]
                    self.options[k] = ["",] + v
                    
                    # calculate width of rowheads 
                    self.options["width"][0] = max([len(str(r)) for r in v])
            
            elif k == "vartype":
                
                s = [str,unicode,"str","string","s", "u","unicode"]
                i = [int, long, "long", "l", "int", "i",]
                f = [float, "float", "f"]
                if v not in s + i + f:
                    svt = [str(v) for v in vartypes]
                    svt = ", ".join(svt)
                    raiseerror(k,svt,"valid values for %s are [%s]")
                
                if v in s:
                    self.options[k] = "s"
                elif v in i:
                    self.options[k] = "i"
                elif v in f:
                    self.options[k] = "f"
            
            elif k == "width":
                if type(v).__name__ in ["int", "float", "long"]:
                    self.options.update({k: [int(v),]*self.columns})
                elif type(v).__name__ in ["list", "tuple"]:
                    if len(v) < self.columns - 1:
                        raiseerror(err="to few width values")
                    else:
                        rheadwidth = [self.options["width"][0],]
                        colwidth = [int(_v) for _v in v[:self.columns-1]]
                        self.options.update({k: rheadwidth + colwidth})
                else:
                    raiseerror(err="unkown type for width")
            
            elif k == "entrystyle" or k == "labelstyle":
                styles = ["flat","raised","sunken","groove","ridge"]
                if v.lower() not in styles:
                    s = ", ".join(styles)
                    err = "valid styles are [%s], got %s" % (s, str(v))
                    raiseerror(err=err)
                
                self.options[k] = v.lower()
            
            elif k == "entrybg" or k == "labelbg":
                if v[0] == "#":
                    valids = "0123456789ABCDEF"
                    for l in v[1:]:
                        if l not in valids:
                            raiseerror(k,v)
                    v = v.upper()
                else:
                    valids = ["white", "black", "red", "green", "blue", 
                              "cyan", "yellow", "magenta" ]
                    if v not in valids:
                        raiseerror(k,v)
                    v = v.lower()
                
                self.options[k] = v
            
            else:
                raiseerror(k, err="unknown option %s!")
            
            Logger.debug("%s ...checked", _c)
        
        Logger.info("%s set options: ", _c)
        for k,v in self.options.items():
            Logger.info("%s  - %s: %s", _c,k,v)
    
    def setupLabel(self, frm, width=10, text=""):
        return ttk.Tkinter.Label(frm,
                                 width=width,
                                 text=text,
                                 relief=self.options["labelstyle"],
                                 bg=self.options["labelbg"],
                                )
    
    def setupColheader(self, columnheads=[]):
        _c = "<%s>" % (self.__class__.__name__,)
        Logger.debug(_c)
        
        if columnheads:
            setOptions(columnhead=True, columnheads=rowheads)
        
        if self.options["columnhead"]:
            for i, col in enumerate(self._matrix[0]):
                col = self.setupLabel(self.frm,
                                      width=self.options["width"][i],
                                      text=self.options["columnheads"][i],
                                     )
                col.grid(row=0, column=i)
            
            Logger.debug("columnheader created")
        else:
            Logger.debug("no columnheader wanted")
    
    def setupRowheader(self, rowheads=[]):
        _c = "<%s>" % (self.__class__.__name__,)
        Logger.debug(_c)
        
        if rowheads:
            setOptions(rowhead=True, rowheads=rowheads)
        
        if self.options["rowhead"]:
            
            for i, row in enumerate(self._matrix):
                row[0] = self.setupLabel(self.frm,
                                         width=self.options["width"][0],
                                         text=self.options["rowheads"][i],
                                         )
                row[0].grid(row=i, column=0)
                
            Logger.debug("rowheader created")
        else:
            Logger.debug("no rowheader wanted")
    
    def setup(self):
        _c = "<%s>" % (self.__class__.__name__,)
        Logger.debug(_c)
        
        self.setupColheader()
        self.setupRowheader()
        
        weiten = self.options["width"]
        
        for z in range(1, self.rows):
            for s in range(1, self.columns):
                if self.options["vartype"] == "s":
                    v = ttk.Tkinter.StringVar()
                elif self.options["vartype"] == "i":
                    v = ttk.Tkinter.IntVar()
                elif self.options["vartype"] == "f":
                    v = ttk.Tkinter.DoubleVar()
                
                e = ttk.Tkinter.Entry(self.frm,
                                      width=self.options["width"][s],
                                      relief=self.options["entrystyle"],
                                      bg=self.options["entrybg"],
                                      textvar=v,
                                     )
                e.grid(row=z, column=s)
                self._matrix[z][s] = (v,e)
                
                Logger.debug("%s cell [row=%s, col=%s] created", _c, z, s)
            
            Logger.debug("%s row %s created", _c, z)
        Logger.debug("%s cells created", _c)
    
    def getCell(self, row, column, entry=False):
        """
        :param row: {1 <= row <= rowcounter}
        :param column: {1 <= column <= rowcounter}
        """
        _c = "<%s>" % (self.__class__.__name__,)
        Logger.debug(_c)
        
        if not (1 <= row < self.rows):
            _c = "<%s>" % (self.__class__.__name__,)
            err = "%s valid row values between 1 and %s" % (_c, self.rows - 1) 
            Logger.exception(err)
            raise MyTableError(err)
        
        if not (1 <= column < self.columns):
            _c = "<%s>" % (self.__class__.__name__,)
            err = "%s valid column values between 1 and %s" \
                % (_c,self.columns-1) 
            Logger.exception(err)
            raise MyTableError(err)
        
        if entry:
            _cell = self._matrix[row][column][1]
        else:
            _cell = self._matrix[row][column][0].get()
        Logger.info("%s return: %s", _c, _cell)
        return _cell
    
    def getRow(self, row, entry=False):
        _c = "<%s>" % (self.__class__.__name__,)
        Logger.debug(_c)
        
        _row = []
        for column in range(1, self.columns):
            _row.append( self.getCell(row, column, entry) )
        
        Logger.info("%s return: [%s]", _c, _row)
        return _row
    
    def get(self, entry=False):
        _c = "<%s>" % (self.__class__.__name__,)
        Logger.debug(_c)
        
        _matrix = []
        for r in range(1, self.rows):
            _matrix.append( self.getRow(r) )
        
        Logger.info("%s return: %s", _c, _matrix)
        return _matrix
    
    def setCell(self, row, column, value):
        _c = "<%s>" % (self.__class__.__name__,)
        Logger.debug("%s set [row=%s, column=%s] to %s", _c,row,column,value)
        
        if not (1 <= row < self.rows):
            _c = "<%s>" % (self.__class__.__name__,)
            err = "%s valid row values between 1 and %s" % (_c, self.rows - 1) 
            Logger.exception(err)
            raise MyTableError(err)
        
        if not (1 <= column < self.columns):
            _c = "<%s>" % (self.__class__.__name__,)
            err = "%s valid column values between 1 and %s" \
                % (_c,self.columns-1) 
            Logger.exception(err)
            raise MyTableError(err)
        
        self._matrix[row][column][0].set( value )
        Logger.info("%s [row=%s, column=%s]=%s", _c, row, column, value)
    
    def __unicode__(self):
        _c = "<%s>" % (self.__class__.__name__,)
        Logger.debug(_c)
        
        weiten = self.options["width"][1:]
        trenner = tuple(["-"*w for w in weiten])
        fmt = ["{:-<%s}" % (w,) for w in weiten]
        fmttr = u"+-%s-+" % ("-+-".join(fmt),)
        tr = fmttr.format(*trenner)
        
        fmt = ["{:<%s}" % (w,) for w in weiten]
        fmt = u"| %s |" % (" | ".join(fmt),)
        
        out = u""
        for r in range(1, self.rows):
            zeile = tuple(self.getRow(r))
            out +=  tr + os.linesep + fmt.format(*zeile) + os.linesep
        return out + tr
    
    def __str__(self):
        return self.__unicode__().encode(ENC)



def test():
    root = ttk.Tkinter.Tk()
    zeilen = 3
    spalten = 4
    tabelle = Table(root,
                    zeilen,
                    spalten,
                    width=[5,10,4,12,9,8,10,11, 9, 12],
                    columnhead=True,
                    columnheads=["S1", "S2", "S3", "S4"],
                    rowhead=True,
                    rowheads=[
                        "Datensatz 1:",
                        "Datensatz 2:",
                        "Datensatz 3:"],
                    entrybg="#ABC",
                    labelbg="#234567"
                    )
    
    for i, z in enumerate(tabelle._matrix[1:]):
        print "Zeile %s: " % str(i + 1),
        print z
    
    tabelle.setCell(1,1, "ASDF")
    print tabelle
    
    root.mainloop()
    return 0


if __name__ == "__main__":
    sys.exit(test())

CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo sedi,

diese beiden Zeilen werden Dir noch viele Probleme machen:

Code: Alles auswählen

        zeile = [None, ]*self.columns
        self._matrix = [zeile,]*self.rows
Du erzeugst nur ein mal die Liste zeile. Alle Zeilen in self._matrix sind also
das selbe Listenelement.

Code: Alles auswählen

>>> zeile = [None]*5
>>> matrix = [zeile]*5
>>> matrix
[[None, None, None, None, None],
 [None, None, None, None, None],
 [None, None, None, None, None],
 [None, None, None, None, None],
 [None, None, None, None, None]]
>>> matrix[0][0]=1
>>> matrix
[[1, None, None, None, None],
 [1, None, None, None, None],
 [1, None, None, None, None],
 [1, None, None, None, None],
 [1, None, None, None, None]]
>>> 
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

Oh wie wahr... - zu später Stunde lässt doch merklich die Konzentration nach

Danke @Sirius3 habs schon ausgebessert
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
Antworten