lundi 22 janvier 2018

ttk.checkbutton in different ttk.LabelFrame : pb in variable object

I've been facing an issue when trying to create some groups of checkbutton in a frame, into several LabelFrame.

I have create a class to create the group of checkbutton (class clsGrpCheckButton, derivated from ttk.CheckButtons). The checkButton information are inside a list of tk.Variable. the method chkGrpGetValue is called whenever the status of a checkbox changed and displays in the console the value in the tk.Variable list

The issue is that depending on the parent of the group, I can get the checkbuttons status or not. The code below showed the issue :

In the first tab, the group of checkbuttons has grid to the same parent, a single frame. The list of checked values is correct. Just check/uncheck the boxes and look at the console. the list is correct

In the second tab, I want to create 3 groups of checkbuttons, in 3 seperate ttk.LabelFrame. Therefore I'm looping and for each "header", I create a ttk.LabelFrame, then a group of checkbuttons which are linked to the header. Then grid these checkbuttons to their ttk.LabelFrame

The result is that : Only the last group created (with header 3, and values more than 30) can be accessed. Just check/unchecked the boxes of the other headers (1 or 2), and you will notice that only the 30th are displayed, not the other one...

here is the reproductible code. Thks for your help

# coding : utf-8

import tkinter as tk
from tkinter import ttk
import pandas as pd


#dummy function to call to test commands
def GF_HasNothingToDo():
    print('nothing to do')
    pass


def GF_AddTab(pParent, pText, pNbRows):
    tab=ttk.Label(pParent)
    pParent.add(tab, text=pText)
    nbR = 0
    while nbR<pNbRows:
        tab.rowconfigure(nbR,weight=1)
        tab.columnconfigure(nbR, weight=1)
        nbR+=1
    return tab

class clsGrpCheckButton(ttk.Checkbutton):
    def __init__(self,
                 pContainer,
                 pLstVal,
                 pCommand,
                 pInitValue=True
                 ): 

    self.grpChk=[0]*len(pLstVal)   
    self.grpKey=[0]*len(pLstVal)
    self.grpLstVal = [0]*len(pLstVal)
    self.grpVariable= [0]*len(pLstVal)
    self.grpActiveKeys = [0]

    for l,t in enumerate(pLstVal):
        #l : index of the list of tuples
        self.grpKey[l] = t[0]
        self.grpLstVal[l] = t[1]
        self.grpVariable[l] = tk.StringVar()
        self.grpChk[l] = ttk.Checkbutton(pContainer, text=self.grpLstVal[l],
                               state='active' ,
                               onvalue= self.grpKey[l], 
                               offvalue= '', 
                               variable =  self.grpVariable[l], 
                               command=pCommand)

        #get default value
        if pInitValue :
            self.grpVariable[l].set(self.grpKey[l])
            self.grpActiveKeys.append(self.grpKey[l])            

#get the index in the list of checkboxes
# depending on the key
def chkGrpGetIdx(self, pKey):
    i=0
    while i <len(self.grpKey):
        if self.grpKey[i]==pKey :
            return i
            i=len(self.grpKey)
        else:
            i+=1

def chkGrpSetValue(self, pKey, pValue):
    #need find correct index first
    i=self.chkGrpGetIdx(pKey)
    self.grpVariable[i] = pValue

#return the list of keys of the group
def chkGrpKeyLst(self):
    return self.grpKey

#return the checkox element of the group of checkox
def chkGrpGetChkObj(self,pKey):
    i=self.chkGrpGetIdx(pKey)
    return self.grpChk[i]

#action when check/uncheck
#at list one element should be active
def chkGrpGetValue(self):
    i=0
    r=len(self.grpVariable)
    self.grpActiveKeys.clear()
    while i < len(self.grpVariable):

        if self.grpVariable[i].get() =='':
            r-=1
        else:
            self.grpActiveKeys.append(self.grpKey[i])
        i+=1         
    if r==0:
        self.grpVariable[0].set(self.grpKey[0])
        self.grpActiveKeys.append(self.grpKey[0])
    print(self.grpActiveKeys)
#to avoid accessing to class attribute directly
def chkGetCheckedValues(self):
    return self.grpActiveKeys


class clsWindows(tk.Tk):
    def __init__(self,
                 pWtitle='',
                 pWidth=800,
                 pHeight=600,
                 pIsResizable=False
                 ): 
    tk.Tk.__init__(self)

    tk.Tk.wm_title(self, pWtitle)
    self.geometry('%sx%s' % (pWidth, pHeight))
    if pIsResizable :
        self.minsize(pWidth, pHeight)

    self.grid_rowconfigure(0, weight = 1)
    self.grid_rowconfigure(1, weight =0)
    self.grid_columnconfigure(0, weight =1)

    self.bt=ttk.Button(self, text='Close',command=lambda:clsWindows.wQuit(self))
    self.bt.grid(row=1, column=0, sticky='se')

    #to make it simple by default, there will be a container 
    #of 10 rows and columns on top of button
    self.rFrame =tk.Frame(self, borderwidth=1, bg = 'blue', relief="ridge")
    self.rFrame.grid(row=0,column=0,sticky='news')
    nbR = 0
    while nbR<10:
        self.rFrame.rowconfigure(nbR, weight=1)
        self.rFrame.columnconfigure(nbR, weight=1)
        nbR+=1

    self.can=tk.Canvas(self.rFrame,bg='white')
    self.can.grid(row=1,column=0, columnspan=3,rowspan=2, sticky='news')
    self.can.create_text(100,10,text='test')

def wQuit(self):
    self.destroy()           

class clsParamWindows(clsWindows): 
    def __init__(self,
                 pWtitle='',
                 pWidth=300,
                 pHeight=300,
                 pIsResizable=False
                 ):  
    clsWindows.__init__(self,
             pWtitle,
             pWidth,
             pHeight,
             pIsResizable
             )    

    self.NotBook = ttk.Notebook(self.rFrame)
    self.NotBook.grid(row=0,column=0,columnspan=10,rowspan=9,sticky='nesw')

    #first tab
    self.tabGeo= GF_AddTab(self.NotBook, 'First tab', 4)
    lstGeo = ['N-East','N-West','East','Central','West', 'South']
    NbCol = 4
    c=0
    r=0
    dic = list(zip(lstGeo,lstGeo))
    self.checkGrpGe0 = clsGrpCheckButton(self.tabGeo,dic,lambda:self.checkGrpGe0.chkGrpGetValue(),True)
    for chk in self.checkGrpGe0.chkGrpKeyLst():
        if not lstGeo.index(chk)%NbCol :
            r+=1
            c=0
        self.checkGrpGe0.chkGrpGetChkObj(chk).grid(row=r, column=c, sticky = 'sw')
        c+=1

    #second tab
    la = [1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3]
    lb= [10,11,12,14,15,20,21,22,23,24,25,26,30,31,32,33]
    lc=['d10','d11','d12','d14','d15','d20','d21','d22','d23','d24','d25','d26','d30','d31','d32','d33']

    df = pd.DataFrame(
        {'DIVISION': la,
         'DEPT_CODE': lb,
         'DEPT_NAME': lc
         })      

    lW = list(zip(df['DIVISION'].astype(str) , df['DEPT_CODE'].astype(str)))
    lpt = list(zip(df['DEPT_CODE'].astype(str) , df['DEPT_NAME'].astype(str)))

    #adding the second tab
    self.tabMD= GF_AddTab(self.NotBook, 'Second tab', 4)        
    c=-1
    curHead = ""
    r=0
    for head, DPT in lW: 
        if not curHead==head:
            curHead = head
            c+=1
            r=0      
            dq=df.query('DIVISION=='+head)
            lpt = list(zip(dq['DEPT_CODE'].astype(str) , dq['DEPT_NAME'].astype(str)))  
            t=ttk.Labelframe(self.tabMD,text=head)
            t.grid(column=c, row=0, sticky='nw') 
        self.checkGrpDept= clsGrpCheckButton(t,lpt,lambda:self.checkGrpDept.chkGrpGetValue(),True)                    
        self.checkGrpDept.chkGrpGetChkObj(DPT).grid(column=c, row=r, sticky='nw')
        t.rowconfigure(r, weight=1)
        t.columnconfigure(c, weight=1)
        r+=1


app = clsParamWindows( pWtitle='Parameters')
app.mainloop()




Aucun commentaire:

Enregistrer un commentaire