dmnfarrell / tkintertable

A pure Python library for adding tables to a Tkinter application

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Table cell value does not update

vnahmias opened this issue · comments

Hello i am facing a really weird issue,

I have a file "editorWindow2.py" which when it's called creates a tk frame containing several tableCanvas :
image

Everything works like a charm i can click on cells and change their values to finally export the whole thing into an xml file.

However sometimes i call the same file from an other entry point in the program but this time the cells are no more editable and i have no clues why. I have checked that i call the window with exactly the same type of objects and data as i do in the case where it works.

Here is the code :

class editionWindowModel2():

    def __init__(self):
        pass

class editionWindowView2():

    def __init__(self,master,count):
        self.frames=[]
        for i in range(count):
            self.frames.append(tk.Frame(master))
            self.frames[i].pack(fill=tk.BOTH, expand=True)



class editionWindowController2():

    def __init__(self,data,count):

        self.root = tk.Tk()
        self.root.geometry("1920x1200")

        self.canvas=tk.Canvas(self.root)
        self.container=tk.Frame(self.canvas)
        self.model=editionWindowModel2()
        self.view=editionWindowView2(self.container,count)

        self.vsb = tk.Scrollbar(self.root, orient="vertical", command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.vsb.set)

        self.vsb.pack(side="right", fill="y")
        self.canvas.pack(side="left", fill=tk.BOTH, expand=True)
        self.canvas.create_window(4,4,window=self.container, anchor="nw", tags="self.container")

        self.container.bind("<Configure>", self.onFrameConfigure)



        self.data=data
        tables=[]#This variable will regroup as many table as the number of metric present in the orginal file
        y=0
        for metric in self.data :
            x=0
            tables.append(TableCanvas(self.view.frames[y],editable=True,width=1700,cellwidth=200,rows=metric.rows,cols=5,rowselectedcolor='#B0E0E6',colselectedcolor='#B0E0E6'))
            colname=tables[y].model.getColumnName(4)
            self.colorCol(tables[y],"#98FB98")
            tables[y].model.columnwidths[colname]=1100
            tables[y].show()
            tables[y].model.setValueAt("Metric ID :",x,0)
            #rest of the table is filled like the line above.

I precise that i am beginner with tkinter so please don't be too hard on me ahah

Let me explain more clearly : my program has two ways to be used, First user can chose to edit an xml file so he can import a file in order to edit it.

class firstWindowView():

    def __init__(self,master):
        self.frame = tk.Frame(master).grid(row=0,column=0,sticky="NSEW")


class firstWindowController():

    def __init__(self):

        self.root = tk.Tk()
        self.model=firstWindowModel()
        self.view=firstWindowView(self.root)

        L1 = tk.Label(self.root, text = "Chose an action :").grid(row=0,column=1)

        importButton=tk.Button(self.root,text="Import from XML",width=20,command=self.importXml).grid(row=0,column=2)
        exportButton=tk.Button(self.root,text="Create your own",width=20,command=self.exportToXml).grid(row=1,column=2)

    def run(self):
        self.root.title("Raspberry Pi Data Sender")
        self.root.mainloop()


    def importXml(self):
        self.root.destroy()
        browser=XmlBrowserController()
        browser.run()



    def exportToXml(self):
        self.root.destroy()
        metricsCreator=metricsController()
        metricsCreator.run()

in case where he choses "import", next window called is this one :

class XmlBrowserView():

    def __init__(self,master):
        self.frame = tk.Frame(master)


class XmlBrowserController():

    def __init__(self):

        self.root = tk.Tk()
        self.model=XmlBrowserModel()
        self.view=XmlBrowserView(self.root)
        self.fileName=tk.StringVar()
        self.fileName =  fd.askopenfilename(initialdir = "../",title = "Select file")

        L1 = tk.Label(self.root, text = "XML file Path : "+self.fileName).grid(row=0,column=0)
        editButton=tk.Button(self.root,text="Edit",width=10,command=self.open).grid(row=0,column=1)
        cancelButton=tk.Button(self.root,text="Cancel",width=10,command=self.quit).grid(row=1,column=1)

    def run(self):
        self.root.title("Load an XML file")
        self.root.mainloop()


    def open(self):

        self.model.fileName=self.fileName
        serializer=Serializer()
        self.model.root=serializer.importFromFile(self.model.fileName)
        display=displayController(self.model.root)
        self.root.destroy()
        display.run()

and finally

class displayView():

    def __init__(self,master):
        self.frame = tk.Frame(master).grid(row=0,column=0,sticky="NSEW")

class displayController():

    def __init__(self,xmlElement):

        self.root = tk.Tk()
        self.root.grid_rowconfigure(0, weight=1)
        self.root.grid_columnconfigure(0, weight=1)
        self.model=displayModel(xmlElement)
        self.tree=xmlElement
        self.view=displayView(self.root)
        self.fileName=tk.StringVar()
        self.count=0

        tree=self.prettify(xmlElement)
        L1 = tk.Label(self.root,justify="left",anchor="w",text = tree).grid(row=0,column=0,sticky="NSEW")

        saveButton2=tk.Button(self.root,text="Edit",width=10,command=self.save2).grid(row=self.count+2,column=0)
        cancelButton=tk.Button(self.root,text="Cancel",width=10,command=self.quit).grid(row=self.count+1,column=0)
    def save2(self):
        root=self.model.xmlElement
        metricsTable=[]
        count=1
        testCounter=0
        for metric in root.findall("{http://standards.iso.org/iso-iec/19086/-2/ed-1/en}Metric"):
            testCounter+=1
            count+=1
            params=[]
            rules=[]
            metricRefs=[]
            expressions=[]
            m=Metric(metric.get('id'),metric.get('source'),metric.get('scale'),metric.get('description'),metric.get('note'),metric.get('category'))
            for param in metric.findall('{http://standards.iso.org/iso-iec/19086/-2/ed-1/en}Parameter'):
                p=Parameter(param.get('id'),param.get('parameterStatement'),param.get('unit'),param.get('description'),param.get('note'))
                params.append(p)
            for expression in metric.findall('{http://standards.iso.org/iso-iec/19086/-2/ed-1/en}Expression'):
                e=Expression(expression.get('id'),expression.get('expressionStatement'),expression.get('expressionLanguage'),expression.get('description'),expression.get('note'),expression.get('unit'))
                expressions.append(e)
            for rule in metric.findall('{http://standards.iso.org/iso-iec/19086/-2/ed-1/en}Rule'):
                r=Rule(rule.get('id'),rule.get('ruleStatement'),rule.get('ruleLanguage'),rule.get('description'),rule.get('note'))
                rules.append(r)
            for ref in metric.findall('{http://standards.iso.org/iso-iec/19086/-2/ed-1/en}UnderlyingMetricRef'):
                reference=UnderlyingMetricRef(ref.get('refid'))
                metricRefs.append(reference)

            m.parameters=params
            m.rules=rules
            m.underlyingMetrics=metricRefs
            m.expression=expressions
            m.rows=self.metricRowsCounter(metric,m)
            metricsTable.append(m)

        #print(type(metricsTable))
        #print(metricsTable)
        self.root.destroy()
        editor=editionWindowController2(metricsTable,count)
        editor.run()

and the last window called is the one i first showed you with the tkintertable.

the other use case changes only in the first windows :

class metricsView():

    def __init__(self,master):
        self.frame = tk.Frame(master)


class metricsController():

    def __init__(self):

        self.root = tk.Tk()
        self.model=metricsModel()
        self.view=metricsView(self.root)

        self.metricList=[]

        newMetricButton=tk.Button(self.root,text="Add a new metric",width=20,command= lambda : self.newMetric(self.metricList)).grid(row=0,column=0)

        printButton=tk.Button(self.root,text="Print your file",width=20,command= lambda : self.toString(self.metricList)).grid(row=0,column=1)

        saveButton=tk.Button(self.root,text="Save",width=20,command=lambda:self.save(self.metricList)).grid(row=0,column=2)

        cancelButton=tk.Button(self.root,text="Quit",width=20,command=self.cancel).grid(row=0,column=3)

    def run(self):
        self.root.title("Metrics Creator")
        self.root.mainloop()

    def toString(self,metrics):
        xmlTree=self.treeBuilder(metrics)
        print(type(xmlTree))
        display=displayController(xmlTree)
        display.run()

and at this point the display is called with an object of the same type as it is in the first case.

Hi @dmnfarrell do you have any news for this bug ?

Did you say you can interact with the table. There is code in tkintertable/Testing.py that tests this very thing and it seems to work. You could take a look at it. It might be a key binding problem but you have given no error messages. It sounds like more to do with your own app.

The thing is that i checked every common mistakes i may have made, i checked the type of what comes out of the display controller and what comes it my editorController in both cases and they are all of the same type. I checked binding of every function and their buttons. In the case where i can't modify the table, i simply can't do anything else.

It would be best if you made a minimal example of 2 tables that works and then add more features into it bit by bit. If a minimal example works then something specific to your app is causing it. Your code is quite long and I can't go through it all in detail.

Hello @dmnfarrell i tried to print every variables in both cases and they have same types each time i don't see what i am missing man. I also have duplicated files for both use cases to try to see a difference between both behavior, i try to modify the second file to get something working but it doesn't here is the code where i can't edit cells :

class editionWindowView2():

    def __init__(self,master,count):
        self.frames=[]
        for i in range(count):
            self.frames.append(tk.Frame(master))
            self.frames[i].pack(fill=tk.BOTH, expand=True)



class editionWindowController2():

    def __init__(self,data,count):

        self.root = tk.Tk()
        self.root.geometry("1920x1200")

        self.model=editionWindowModel2()
        self.view=editionWindowView2(self.root,count)

        y=0
        self.data=data
        tables=[]#This variable will regroup as many table as the number of metric present in the orginal file
        for i in range(len(self.data)) :
            table=(TableCanvas(self.view.frames[i],editable=True,width=1700,cellwidth=200,rows=data[i].rows,cols=5,rowselectedcolor='#B0E0E6',colselectedcolor='#B0E0E6'))
            table.show()
            tables.append(table)

        for metric in self.data :
            x=0
            tables[y].model.setValueAt("Metric ID :",x,0)
            tables[y].model.setValueAt("Metric Source :",x+1,0)
            tables[y].model.setValueAt("Metric Scale :",x+2,0)

            tables[y].model.setValueAt(metric.id,x,1)
            tables[y].model.setValueAt(metric.source,x+1,2)
            tables[y].model.setValueAt(metric.scale,x+2,2)


            x+=3
            if(metric.description!=None):
                tables[y].model.setValueAt("Metric Description :",x,0)
                tables[y].model.setValueAt(metric.description,x,2)
                x+=1
            elif(metric.category!=None):
                tables[y].model.setValueAt("Metric Category :",x,0)
                tables[y].model.setValueAt(metric.category,x,2)
                x+=1
            for expression in metric.expression:
                tables[y].model.setValueAt("Expression ID :",x,0)
                tables[y].model.setValueAt("Expression Statement :",x+1,0)
                tables[y].model.setValueAt("Expression Language:",x+2,0)
                tables[y].model.setValueAt(expression.id,x,3)
                tables[y].model.setValueAt(expression.expressionStatement,x+1,4)
                tables[y].model.setValueAt(expression.expressionLanguage,x+2,4)
                x+=3
                if(expression.description!=None):
                    tables[y].model.setValueAt("Expression Description :",x,0)
                    tables[y].model.setValueAt(expression.description,x,4)
                    x+=1
                elif(expression.note!=None):
                    tables[y].model.setValueAt("Expression Note :",x,0)
                    tables[y].model.setValueAt(expression.note,x,4)
                    x+=1
                elif(expression.unit!=None):
                    tables[y].model.setValueAt("Expression Unit :",x,0)
                    tables[y].model.setValueAt(expression.unit,x,4)
                    x+=1
            try:
                for param in metric.parameters:
                    tables[y].model.setValueAt("Parameter ID :",x,0)
                    tables[y].model.setValueAt("Parameter Statement :",x+1,0)
                    tables[y].model.setValueAt("Parameter Unit :",x+2,0)
                    tables[y].model.setValueAt(param.id,x,3)
                    tables[y].model.setValueAt(param.parameterStatement,x+1,4)
                    tables[y].model.setValueAt(param.unit,x+2,4)
                    x=x+3
                    if(param.description!=None):
                        tables[y].model.setValueAt("Parameter Description :",x,0)
                        tables[y].model.setValueAt(param.description,x,4)
                        x+=1
                    elif(param.note!=None):
                        tables[y].model.setValueAt("Parameter Note :",x,0)
                        tables[y].model.setValueAt(param.note,x,4)
                        x+=1
            except :
                pass
            try:
                for rule in metric.rules:
                    tables[y].model.setValueAt("Rule ID :",x,0)
                    tables[y].model.setValueAt("Rule Statement :",x+1,0)
                    tables[y].model.setValueAt("Rule Language :",x+2,0)
                    tables[y].model.setValueAt(rule.id,x,3)
                    tables[y].model.setValueAt(rule.ruleStatement,x+1,4)
                    tables[y].model.setValueAt(rule.ruleLanguage,x+2,4)

                    x=x+3
                    if(rule.description!=None):
                        tables[y].model.setValueAt("Rule Description :",x,0)
                        tables[y].model.setValueAt(rule.description,x,4)
                        x+=1
                    elif(rule.note!=None):
                        tables[y].model.setValueAt("Rule Note :",x,0)
                        tables[y].model.setValueAt(rule.note,x,4)
                        x+=1
            except :
                pass
            try:
                for ref in metric.underlyingMetrics:
                    tables[y].model.setValueAt("UnderlyingMetricRef :",x,0)
                    tables[y].model.setValueAt(ref.refid,x,3)
                    x=x+1
            except :
                pass
            colname=tables[y].model.getColumnName(4)
            self.colorCol(tables[y],"#98FB98")
            tables[y].model.columnwidths[colname]=1100
            tables[y].show()

            y+=1



        exportButton=tk.Button(self.root,text="Export To Xml",width=15,command=lambda: self.exportToXml(tables)).pack()
        cancelButton=tk.Button(self.root,text="Quit",width=15,command=self.quit).pack()

I have finally found what causes cells not alterable, in file MetricsController function toString(), i had not wrote the line self.root.destroy() which closes previous window before dsiplaying the new one, because wanted to be able to come back to that previous window. I think there is some kind of conflict between tkintertable and tk loops or something like this. Please if you have some time take a look at it and if you find what is wrong please keep me updated i wanna know !

So if your change fixes the problem why do you still think tkintertable cause a conflict of some kind? there is only one tkinter thread running at once and all the widgets run in that I believe.