communication between Checkbutton and Entry(box)












0















from tkinter import *
import pandas as pd

df = pd.DataFrame({'item': list('abcde'), 'default_vals': [2,6,4,5,1]})

def input_data(df):
box = Tk()
height = str(int(25*(df.shape[0]+2)))
box.geometry("320x" + height)
box.title("my box")

#initialise
params, checkButtons, intVars = , ,
default_vals = list(df.default_vals)
itemList = list(df.item)

for i,label in enumerate(itemList):
Label(box, text = label).grid(row = i, sticky = W)
params.append(Entry(box))
params[-1].grid(row = i, column = 1)
params[-1].insert(i, default_vals[i])
intVars.append(IntVar())
checkButtons.append(Checkbutton(variable = intVars[-1]))
checkButtons[-1].grid(row = i, column = 3)

def sumbit(event=None):
global fields, checked
fields = [params[i].get() for i in range(len(params))]
checked = [intVars[i].get() for i in range(len(intVars))]
box.destroy()

#add submit button
box.bind('<Return>', sumbit)
Button(box, text = "submit",
command = sumbit).grid(row = df.shape[0]+3, sticky = W)
box.focus_force()
mainloop()

return fields, checked


I am new to tkinter and not sure what I a trying to do is possible.



At present, my script (simplified here to a function rather than a class) builds a box with all the default values entered in the fields:



enter image description here



Instead, I want to start with empty fields which, once the corresponding checkButton is clicked will get the default value (should still be able to manually change it through the field as happens now), and also, once any value is entered in a given field, the corresponding checkButton is selected.



Are these possible?










share|improve this question





























    0















    from tkinter import *
    import pandas as pd

    df = pd.DataFrame({'item': list('abcde'), 'default_vals': [2,6,4,5,1]})

    def input_data(df):
    box = Tk()
    height = str(int(25*(df.shape[0]+2)))
    box.geometry("320x" + height)
    box.title("my box")

    #initialise
    params, checkButtons, intVars = , ,
    default_vals = list(df.default_vals)
    itemList = list(df.item)

    for i,label in enumerate(itemList):
    Label(box, text = label).grid(row = i, sticky = W)
    params.append(Entry(box))
    params[-1].grid(row = i, column = 1)
    params[-1].insert(i, default_vals[i])
    intVars.append(IntVar())
    checkButtons.append(Checkbutton(variable = intVars[-1]))
    checkButtons[-1].grid(row = i, column = 3)

    def sumbit(event=None):
    global fields, checked
    fields = [params[i].get() for i in range(len(params))]
    checked = [intVars[i].get() for i in range(len(intVars))]
    box.destroy()

    #add submit button
    box.bind('<Return>', sumbit)
    Button(box, text = "submit",
    command = sumbit).grid(row = df.shape[0]+3, sticky = W)
    box.focus_force()
    mainloop()

    return fields, checked


    I am new to tkinter and not sure what I a trying to do is possible.



    At present, my script (simplified here to a function rather than a class) builds a box with all the default values entered in the fields:



    enter image description here



    Instead, I want to start with empty fields which, once the corresponding checkButton is clicked will get the default value (should still be able to manually change it through the field as happens now), and also, once any value is entered in a given field, the corresponding checkButton is selected.



    Are these possible?










    share|improve this question



























      0












      0








      0








      from tkinter import *
      import pandas as pd

      df = pd.DataFrame({'item': list('abcde'), 'default_vals': [2,6,4,5,1]})

      def input_data(df):
      box = Tk()
      height = str(int(25*(df.shape[0]+2)))
      box.geometry("320x" + height)
      box.title("my box")

      #initialise
      params, checkButtons, intVars = , ,
      default_vals = list(df.default_vals)
      itemList = list(df.item)

      for i,label in enumerate(itemList):
      Label(box, text = label).grid(row = i, sticky = W)
      params.append(Entry(box))
      params[-1].grid(row = i, column = 1)
      params[-1].insert(i, default_vals[i])
      intVars.append(IntVar())
      checkButtons.append(Checkbutton(variable = intVars[-1]))
      checkButtons[-1].grid(row = i, column = 3)

      def sumbit(event=None):
      global fields, checked
      fields = [params[i].get() for i in range(len(params))]
      checked = [intVars[i].get() for i in range(len(intVars))]
      box.destroy()

      #add submit button
      box.bind('<Return>', sumbit)
      Button(box, text = "submit",
      command = sumbit).grid(row = df.shape[0]+3, sticky = W)
      box.focus_force()
      mainloop()

      return fields, checked


      I am new to tkinter and not sure what I a trying to do is possible.



      At present, my script (simplified here to a function rather than a class) builds a box with all the default values entered in the fields:



      enter image description here



      Instead, I want to start with empty fields which, once the corresponding checkButton is clicked will get the default value (should still be able to manually change it through the field as happens now), and also, once any value is entered in a given field, the corresponding checkButton is selected.



      Are these possible?










      share|improve this question
















      from tkinter import *
      import pandas as pd

      df = pd.DataFrame({'item': list('abcde'), 'default_vals': [2,6,4,5,1]})

      def input_data(df):
      box = Tk()
      height = str(int(25*(df.shape[0]+2)))
      box.geometry("320x" + height)
      box.title("my box")

      #initialise
      params, checkButtons, intVars = , ,
      default_vals = list(df.default_vals)
      itemList = list(df.item)

      for i,label in enumerate(itemList):
      Label(box, text = label).grid(row = i, sticky = W)
      params.append(Entry(box))
      params[-1].grid(row = i, column = 1)
      params[-1].insert(i, default_vals[i])
      intVars.append(IntVar())
      checkButtons.append(Checkbutton(variable = intVars[-1]))
      checkButtons[-1].grid(row = i, column = 3)

      def sumbit(event=None):
      global fields, checked
      fields = [params[i].get() for i in range(len(params))]
      checked = [intVars[i].get() for i in range(len(intVars))]
      box.destroy()

      #add submit button
      box.bind('<Return>', sumbit)
      Button(box, text = "submit",
      command = sumbit).grid(row = df.shape[0]+3, sticky = W)
      box.focus_force()
      mainloop()

      return fields, checked


      I am new to tkinter and not sure what I a trying to do is possible.



      At present, my script (simplified here to a function rather than a class) builds a box with all the default values entered in the fields:



      enter image description here



      Instead, I want to start with empty fields which, once the corresponding checkButton is clicked will get the default value (should still be able to manually change it through the field as happens now), and also, once any value is entered in a given field, the corresponding checkButton is selected.



      Are these possible?







      python tkinter






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 21 '18 at 18:49









      eyllanesc

      84.3k103562




      84.3k103562










      asked Nov 21 '18 at 16:43









      TonyTony

      414314




      414314
























          1 Answer
          1






          active

          oldest

          votes


















          1














          It is possible, but let me preface my solution with a few cautions on your current code:





          1. It's rarely advisable to do a star import (from tkinter import *) as you don't have any control over what gets imported into your namespace. It's more advisable to explicitly import what you need as a reference:



            import tkinter as tk

            tk.Label() # same as if you wrote Label()
            tk.IntVar() # same as if you called IntVar()


          2. The behaviour you wanted, while possible, might not be necessarily user friendly. What happens when a user has already entered something, and unchecks the checkbox? Or what happens if the checkbox was selected and then the user deleted the information? These might be things you want to think about.



          Having said that, the solution is to use add a trace callback function over your variable(s). You'll also need to add a StringVar() for the Entry boxes as you wanted a two way connection:



          # add strVars as a list of StringVar() for your Entry box
          params, checkButtons, intVars, strVars = , , ,


          During your iteration of enumerate(itemList), add these:



          # Create new StringVar()
          strVars.append(StringVar())

          # add a trace callback for tracking changes over the StringVar()
          strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))

          # update your Entry to set textvariable to the new strVar
          params.append(Entry(box, textvariable=strVars[-1]))


          # similarly, add a trace for your IntVar
          intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))


          You'll need to define the two trace callback functions before you iterate through the widget creations:



          def trace_intVar(idx):

          # if Checkbox is checked and Entry is empty...
          if intVars[idx].get() and not params[idx].get():

          # prefill Entry with default value
          params[idx].insert(0, df.default_vals[idx])

          def trace_strVar(idx):

          # if Entry has something...
          if strVars[idx].get():

          # and Checkbox is not checked...
          if not intVars[idx].get():

          # Set the checkbox to checked.
          intVars[idx].set(True)

          # but if Entry is empty...
          else:

          # Set the Checkbox to uncheck.
          intVars[idx].set(False)


          Remember I mentioned the behaviour - I took a little liberty to clear the Checkbox if Entry is empty. If you however don't wish to do that, you'll need to modify the handling a little.



          Note on the way the trace_add is written. The callback function is always passed with three default arguments, namely the Variable Name, The Variable Index (if any) and Operation (see this great answer from Bryan Oakley). Since we don't need any in this case (we can't reverse reference the variable name to the linked index between the variable lists), we'll have to manually wrap the callback with another lambda and ignore the three arguments:



          lambda var,        # reserve first pos for variable name
          var_idx, # reserve second pos for variable index
          oper, # reserve third pos for operation
          idx=i: # pass in i by reference for indexing point
          trace_intVar(idx) # only pass in the idx


          You cannot just pass lambda...: trace_intVar(i) as i will be passed by value instead of reference in that case. Trust me, I've made this error before. Therefore we pass another argument idx with its default set to i, which will now be passed by reference.



          If trace_add doesn't work, use trace('w', ...) instead.





          For prosperity, here's the complete implemented solution to your question:



          from tkinter import *
          import pandas as pd

          df = pd.DataFrame({'item': list('abcde'), 'default_vals': [2,6,4,5,1]})

          def input_data(df):
          box = Tk()
          height = str(int(25*(df.shape[0]+2)))
          box.geometry("320x" + height)
          box.title("my box")

          #initialise
          params, checkButtons, intVars, strVars = , , ,
          default_vals = list(df.default_vals)
          itemList = list(df.item)

          def trace_intVar(idx):
          if intVars[idx].get() and not params[idx].get():
          params[idx].insert(0, df.default_vals[idx])

          def trace_strVar(idx):
          if strVars[idx].get():
          if not intVars[idx].get():
          intVars[idx].set(True)
          else:
          intVars[idx].set(False)


          for i,label in enumerate(itemList):
          Label(box, text = label).grid(row = i, sticky = W)
          strVars.append(StringVar())
          strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))
          params.append(Entry(box, textvariable=strVars[-1]))
          params[-1].grid(row = i, column = 1)
          #params[-1].insert(i, default_vals[i]) # <-- You don't need this any more
          intVars.append(IntVar())
          intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))
          checkButtons.append(Checkbutton(variable = intVars[-1]))
          checkButtons[-1].grid(row = i, column = 3)



          def sumbit(event=None):
          global fields, checked
          fields = [params[i].get() for i in range(len(params))]
          checked = [intVars[i].get() for i in range(len(intVars))]
          box.destroy()

          #add submit button
          box.bind('<Return>', sumbit)
          Button(box, text = "submit",
          command = sumbit).grid(row = df.shape[0]+3, sticky = W)
          box.focus_force()
          mainloop()

          return fields, checked





          share|improve this answer


























          • Thank you very much for your response! I will go through it step by step to understand what you're doing but on a first note, copying, pasting and running the complete implementation you're providing in the end produces the following error:AttributeError: 'StringVar' object has no attribute 'trace_add' Would you care to double check please?

            – Tony
            Nov 21 '18 at 18:46













          • What version of Python/Tkinter are you using? It works on mine (Python 3.7.1). Are you on Python 2.7?

            – Idlehands
            Nov 21 '18 at 18:50













          • Try trace('w', ...) instead of trace_add('write', ...) and see if it works? Change it for both the strVars and intVars.

            – Idlehands
            Nov 21 '18 at 18:52













          • yes it worked like magic! thank you. I will go through the code now to see if I get it. I have Python 3.5, not sure about tkinter as .__version__ isn't available. Was it a tkinter version issue then? Based on what did you change trace_add('write',..) to trace('w'..) ? Thanks again

            – Tony
            Nov 21 '18 at 18:58






          • 1





            I forgot where I came across trace_add and can't find it at the moment, but I believe it's to replace trace in future versions (perhaps I got a deprecation warning somewhere). Here's the relevant tkinter documentation on trace, and here's a relevant bug report on the proposal to add the various trace_... methods.

            – Idlehands
            Nov 21 '18 at 19:10













          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53416804%2fcommunication-between-checkbutton-and-entrybox%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          It is possible, but let me preface my solution with a few cautions on your current code:





          1. It's rarely advisable to do a star import (from tkinter import *) as you don't have any control over what gets imported into your namespace. It's more advisable to explicitly import what you need as a reference:



            import tkinter as tk

            tk.Label() # same as if you wrote Label()
            tk.IntVar() # same as if you called IntVar()


          2. The behaviour you wanted, while possible, might not be necessarily user friendly. What happens when a user has already entered something, and unchecks the checkbox? Or what happens if the checkbox was selected and then the user deleted the information? These might be things you want to think about.



          Having said that, the solution is to use add a trace callback function over your variable(s). You'll also need to add a StringVar() for the Entry boxes as you wanted a two way connection:



          # add strVars as a list of StringVar() for your Entry box
          params, checkButtons, intVars, strVars = , , ,


          During your iteration of enumerate(itemList), add these:



          # Create new StringVar()
          strVars.append(StringVar())

          # add a trace callback for tracking changes over the StringVar()
          strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))

          # update your Entry to set textvariable to the new strVar
          params.append(Entry(box, textvariable=strVars[-1]))


          # similarly, add a trace for your IntVar
          intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))


          You'll need to define the two trace callback functions before you iterate through the widget creations:



          def trace_intVar(idx):

          # if Checkbox is checked and Entry is empty...
          if intVars[idx].get() and not params[idx].get():

          # prefill Entry with default value
          params[idx].insert(0, df.default_vals[idx])

          def trace_strVar(idx):

          # if Entry has something...
          if strVars[idx].get():

          # and Checkbox is not checked...
          if not intVars[idx].get():

          # Set the checkbox to checked.
          intVars[idx].set(True)

          # but if Entry is empty...
          else:

          # Set the Checkbox to uncheck.
          intVars[idx].set(False)


          Remember I mentioned the behaviour - I took a little liberty to clear the Checkbox if Entry is empty. If you however don't wish to do that, you'll need to modify the handling a little.



          Note on the way the trace_add is written. The callback function is always passed with three default arguments, namely the Variable Name, The Variable Index (if any) and Operation (see this great answer from Bryan Oakley). Since we don't need any in this case (we can't reverse reference the variable name to the linked index between the variable lists), we'll have to manually wrap the callback with another lambda and ignore the three arguments:



          lambda var,        # reserve first pos for variable name
          var_idx, # reserve second pos for variable index
          oper, # reserve third pos for operation
          idx=i: # pass in i by reference for indexing point
          trace_intVar(idx) # only pass in the idx


          You cannot just pass lambda...: trace_intVar(i) as i will be passed by value instead of reference in that case. Trust me, I've made this error before. Therefore we pass another argument idx with its default set to i, which will now be passed by reference.



          If trace_add doesn't work, use trace('w', ...) instead.





          For prosperity, here's the complete implemented solution to your question:



          from tkinter import *
          import pandas as pd

          df = pd.DataFrame({'item': list('abcde'), 'default_vals': [2,6,4,5,1]})

          def input_data(df):
          box = Tk()
          height = str(int(25*(df.shape[0]+2)))
          box.geometry("320x" + height)
          box.title("my box")

          #initialise
          params, checkButtons, intVars, strVars = , , ,
          default_vals = list(df.default_vals)
          itemList = list(df.item)

          def trace_intVar(idx):
          if intVars[idx].get() and not params[idx].get():
          params[idx].insert(0, df.default_vals[idx])

          def trace_strVar(idx):
          if strVars[idx].get():
          if not intVars[idx].get():
          intVars[idx].set(True)
          else:
          intVars[idx].set(False)


          for i,label in enumerate(itemList):
          Label(box, text = label).grid(row = i, sticky = W)
          strVars.append(StringVar())
          strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))
          params.append(Entry(box, textvariable=strVars[-1]))
          params[-1].grid(row = i, column = 1)
          #params[-1].insert(i, default_vals[i]) # <-- You don't need this any more
          intVars.append(IntVar())
          intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))
          checkButtons.append(Checkbutton(variable = intVars[-1]))
          checkButtons[-1].grid(row = i, column = 3)



          def sumbit(event=None):
          global fields, checked
          fields = [params[i].get() for i in range(len(params))]
          checked = [intVars[i].get() for i in range(len(intVars))]
          box.destroy()

          #add submit button
          box.bind('<Return>', sumbit)
          Button(box, text = "submit",
          command = sumbit).grid(row = df.shape[0]+3, sticky = W)
          box.focus_force()
          mainloop()

          return fields, checked





          share|improve this answer


























          • Thank you very much for your response! I will go through it step by step to understand what you're doing but on a first note, copying, pasting and running the complete implementation you're providing in the end produces the following error:AttributeError: 'StringVar' object has no attribute 'trace_add' Would you care to double check please?

            – Tony
            Nov 21 '18 at 18:46













          • What version of Python/Tkinter are you using? It works on mine (Python 3.7.1). Are you on Python 2.7?

            – Idlehands
            Nov 21 '18 at 18:50













          • Try trace('w', ...) instead of trace_add('write', ...) and see if it works? Change it for both the strVars and intVars.

            – Idlehands
            Nov 21 '18 at 18:52













          • yes it worked like magic! thank you. I will go through the code now to see if I get it. I have Python 3.5, not sure about tkinter as .__version__ isn't available. Was it a tkinter version issue then? Based on what did you change trace_add('write',..) to trace('w'..) ? Thanks again

            – Tony
            Nov 21 '18 at 18:58






          • 1





            I forgot where I came across trace_add and can't find it at the moment, but I believe it's to replace trace in future versions (perhaps I got a deprecation warning somewhere). Here's the relevant tkinter documentation on trace, and here's a relevant bug report on the proposal to add the various trace_... methods.

            – Idlehands
            Nov 21 '18 at 19:10


















          1














          It is possible, but let me preface my solution with a few cautions on your current code:





          1. It's rarely advisable to do a star import (from tkinter import *) as you don't have any control over what gets imported into your namespace. It's more advisable to explicitly import what you need as a reference:



            import tkinter as tk

            tk.Label() # same as if you wrote Label()
            tk.IntVar() # same as if you called IntVar()


          2. The behaviour you wanted, while possible, might not be necessarily user friendly. What happens when a user has already entered something, and unchecks the checkbox? Or what happens if the checkbox was selected and then the user deleted the information? These might be things you want to think about.



          Having said that, the solution is to use add a trace callback function over your variable(s). You'll also need to add a StringVar() for the Entry boxes as you wanted a two way connection:



          # add strVars as a list of StringVar() for your Entry box
          params, checkButtons, intVars, strVars = , , ,


          During your iteration of enumerate(itemList), add these:



          # Create new StringVar()
          strVars.append(StringVar())

          # add a trace callback for tracking changes over the StringVar()
          strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))

          # update your Entry to set textvariable to the new strVar
          params.append(Entry(box, textvariable=strVars[-1]))


          # similarly, add a trace for your IntVar
          intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))


          You'll need to define the two trace callback functions before you iterate through the widget creations:



          def trace_intVar(idx):

          # if Checkbox is checked and Entry is empty...
          if intVars[idx].get() and not params[idx].get():

          # prefill Entry with default value
          params[idx].insert(0, df.default_vals[idx])

          def trace_strVar(idx):

          # if Entry has something...
          if strVars[idx].get():

          # and Checkbox is not checked...
          if not intVars[idx].get():

          # Set the checkbox to checked.
          intVars[idx].set(True)

          # but if Entry is empty...
          else:

          # Set the Checkbox to uncheck.
          intVars[idx].set(False)


          Remember I mentioned the behaviour - I took a little liberty to clear the Checkbox if Entry is empty. If you however don't wish to do that, you'll need to modify the handling a little.



          Note on the way the trace_add is written. The callback function is always passed with three default arguments, namely the Variable Name, The Variable Index (if any) and Operation (see this great answer from Bryan Oakley). Since we don't need any in this case (we can't reverse reference the variable name to the linked index between the variable lists), we'll have to manually wrap the callback with another lambda and ignore the three arguments:



          lambda var,        # reserve first pos for variable name
          var_idx, # reserve second pos for variable index
          oper, # reserve third pos for operation
          idx=i: # pass in i by reference for indexing point
          trace_intVar(idx) # only pass in the idx


          You cannot just pass lambda...: trace_intVar(i) as i will be passed by value instead of reference in that case. Trust me, I've made this error before. Therefore we pass another argument idx with its default set to i, which will now be passed by reference.



          If trace_add doesn't work, use trace('w', ...) instead.





          For prosperity, here's the complete implemented solution to your question:



          from tkinter import *
          import pandas as pd

          df = pd.DataFrame({'item': list('abcde'), 'default_vals': [2,6,4,5,1]})

          def input_data(df):
          box = Tk()
          height = str(int(25*(df.shape[0]+2)))
          box.geometry("320x" + height)
          box.title("my box")

          #initialise
          params, checkButtons, intVars, strVars = , , ,
          default_vals = list(df.default_vals)
          itemList = list(df.item)

          def trace_intVar(idx):
          if intVars[idx].get() and not params[idx].get():
          params[idx].insert(0, df.default_vals[idx])

          def trace_strVar(idx):
          if strVars[idx].get():
          if not intVars[idx].get():
          intVars[idx].set(True)
          else:
          intVars[idx].set(False)


          for i,label in enumerate(itemList):
          Label(box, text = label).grid(row = i, sticky = W)
          strVars.append(StringVar())
          strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))
          params.append(Entry(box, textvariable=strVars[-1]))
          params[-1].grid(row = i, column = 1)
          #params[-1].insert(i, default_vals[i]) # <-- You don't need this any more
          intVars.append(IntVar())
          intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))
          checkButtons.append(Checkbutton(variable = intVars[-1]))
          checkButtons[-1].grid(row = i, column = 3)



          def sumbit(event=None):
          global fields, checked
          fields = [params[i].get() for i in range(len(params))]
          checked = [intVars[i].get() for i in range(len(intVars))]
          box.destroy()

          #add submit button
          box.bind('<Return>', sumbit)
          Button(box, text = "submit",
          command = sumbit).grid(row = df.shape[0]+3, sticky = W)
          box.focus_force()
          mainloop()

          return fields, checked





          share|improve this answer


























          • Thank you very much for your response! I will go through it step by step to understand what you're doing but on a first note, copying, pasting and running the complete implementation you're providing in the end produces the following error:AttributeError: 'StringVar' object has no attribute 'trace_add' Would you care to double check please?

            – Tony
            Nov 21 '18 at 18:46













          • What version of Python/Tkinter are you using? It works on mine (Python 3.7.1). Are you on Python 2.7?

            – Idlehands
            Nov 21 '18 at 18:50













          • Try trace('w', ...) instead of trace_add('write', ...) and see if it works? Change it for both the strVars and intVars.

            – Idlehands
            Nov 21 '18 at 18:52













          • yes it worked like magic! thank you. I will go through the code now to see if I get it. I have Python 3.5, not sure about tkinter as .__version__ isn't available. Was it a tkinter version issue then? Based on what did you change trace_add('write',..) to trace('w'..) ? Thanks again

            – Tony
            Nov 21 '18 at 18:58






          • 1





            I forgot where I came across trace_add and can't find it at the moment, but I believe it's to replace trace in future versions (perhaps I got a deprecation warning somewhere). Here's the relevant tkinter documentation on trace, and here's a relevant bug report on the proposal to add the various trace_... methods.

            – Idlehands
            Nov 21 '18 at 19:10
















          1












          1








          1







          It is possible, but let me preface my solution with a few cautions on your current code:





          1. It's rarely advisable to do a star import (from tkinter import *) as you don't have any control over what gets imported into your namespace. It's more advisable to explicitly import what you need as a reference:



            import tkinter as tk

            tk.Label() # same as if you wrote Label()
            tk.IntVar() # same as if you called IntVar()


          2. The behaviour you wanted, while possible, might not be necessarily user friendly. What happens when a user has already entered something, and unchecks the checkbox? Or what happens if the checkbox was selected and then the user deleted the information? These might be things you want to think about.



          Having said that, the solution is to use add a trace callback function over your variable(s). You'll also need to add a StringVar() for the Entry boxes as you wanted a two way connection:



          # add strVars as a list of StringVar() for your Entry box
          params, checkButtons, intVars, strVars = , , ,


          During your iteration of enumerate(itemList), add these:



          # Create new StringVar()
          strVars.append(StringVar())

          # add a trace callback for tracking changes over the StringVar()
          strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))

          # update your Entry to set textvariable to the new strVar
          params.append(Entry(box, textvariable=strVars[-1]))


          # similarly, add a trace for your IntVar
          intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))


          You'll need to define the two trace callback functions before you iterate through the widget creations:



          def trace_intVar(idx):

          # if Checkbox is checked and Entry is empty...
          if intVars[idx].get() and not params[idx].get():

          # prefill Entry with default value
          params[idx].insert(0, df.default_vals[idx])

          def trace_strVar(idx):

          # if Entry has something...
          if strVars[idx].get():

          # and Checkbox is not checked...
          if not intVars[idx].get():

          # Set the checkbox to checked.
          intVars[idx].set(True)

          # but if Entry is empty...
          else:

          # Set the Checkbox to uncheck.
          intVars[idx].set(False)


          Remember I mentioned the behaviour - I took a little liberty to clear the Checkbox if Entry is empty. If you however don't wish to do that, you'll need to modify the handling a little.



          Note on the way the trace_add is written. The callback function is always passed with three default arguments, namely the Variable Name, The Variable Index (if any) and Operation (see this great answer from Bryan Oakley). Since we don't need any in this case (we can't reverse reference the variable name to the linked index between the variable lists), we'll have to manually wrap the callback with another lambda and ignore the three arguments:



          lambda var,        # reserve first pos for variable name
          var_idx, # reserve second pos for variable index
          oper, # reserve third pos for operation
          idx=i: # pass in i by reference for indexing point
          trace_intVar(idx) # only pass in the idx


          You cannot just pass lambda...: trace_intVar(i) as i will be passed by value instead of reference in that case. Trust me, I've made this error before. Therefore we pass another argument idx with its default set to i, which will now be passed by reference.



          If trace_add doesn't work, use trace('w', ...) instead.





          For prosperity, here's the complete implemented solution to your question:



          from tkinter import *
          import pandas as pd

          df = pd.DataFrame({'item': list('abcde'), 'default_vals': [2,6,4,5,1]})

          def input_data(df):
          box = Tk()
          height = str(int(25*(df.shape[0]+2)))
          box.geometry("320x" + height)
          box.title("my box")

          #initialise
          params, checkButtons, intVars, strVars = , , ,
          default_vals = list(df.default_vals)
          itemList = list(df.item)

          def trace_intVar(idx):
          if intVars[idx].get() and not params[idx].get():
          params[idx].insert(0, df.default_vals[idx])

          def trace_strVar(idx):
          if strVars[idx].get():
          if not intVars[idx].get():
          intVars[idx].set(True)
          else:
          intVars[idx].set(False)


          for i,label in enumerate(itemList):
          Label(box, text = label).grid(row = i, sticky = W)
          strVars.append(StringVar())
          strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))
          params.append(Entry(box, textvariable=strVars[-1]))
          params[-1].grid(row = i, column = 1)
          #params[-1].insert(i, default_vals[i]) # <-- You don't need this any more
          intVars.append(IntVar())
          intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))
          checkButtons.append(Checkbutton(variable = intVars[-1]))
          checkButtons[-1].grid(row = i, column = 3)



          def sumbit(event=None):
          global fields, checked
          fields = [params[i].get() for i in range(len(params))]
          checked = [intVars[i].get() for i in range(len(intVars))]
          box.destroy()

          #add submit button
          box.bind('<Return>', sumbit)
          Button(box, text = "submit",
          command = sumbit).grid(row = df.shape[0]+3, sticky = W)
          box.focus_force()
          mainloop()

          return fields, checked





          share|improve this answer















          It is possible, but let me preface my solution with a few cautions on your current code:





          1. It's rarely advisable to do a star import (from tkinter import *) as you don't have any control over what gets imported into your namespace. It's more advisable to explicitly import what you need as a reference:



            import tkinter as tk

            tk.Label() # same as if you wrote Label()
            tk.IntVar() # same as if you called IntVar()


          2. The behaviour you wanted, while possible, might not be necessarily user friendly. What happens when a user has already entered something, and unchecks the checkbox? Or what happens if the checkbox was selected and then the user deleted the information? These might be things you want to think about.



          Having said that, the solution is to use add a trace callback function over your variable(s). You'll also need to add a StringVar() for the Entry boxes as you wanted a two way connection:



          # add strVars as a list of StringVar() for your Entry box
          params, checkButtons, intVars, strVars = , , ,


          During your iteration of enumerate(itemList), add these:



          # Create new StringVar()
          strVars.append(StringVar())

          # add a trace callback for tracking changes over the StringVar()
          strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))

          # update your Entry to set textvariable to the new strVar
          params.append(Entry(box, textvariable=strVars[-1]))


          # similarly, add a trace for your IntVar
          intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))


          You'll need to define the two trace callback functions before you iterate through the widget creations:



          def trace_intVar(idx):

          # if Checkbox is checked and Entry is empty...
          if intVars[idx].get() and not params[idx].get():

          # prefill Entry with default value
          params[idx].insert(0, df.default_vals[idx])

          def trace_strVar(idx):

          # if Entry has something...
          if strVars[idx].get():

          # and Checkbox is not checked...
          if not intVars[idx].get():

          # Set the checkbox to checked.
          intVars[idx].set(True)

          # but if Entry is empty...
          else:

          # Set the Checkbox to uncheck.
          intVars[idx].set(False)


          Remember I mentioned the behaviour - I took a little liberty to clear the Checkbox if Entry is empty. If you however don't wish to do that, you'll need to modify the handling a little.



          Note on the way the trace_add is written. The callback function is always passed with three default arguments, namely the Variable Name, The Variable Index (if any) and Operation (see this great answer from Bryan Oakley). Since we don't need any in this case (we can't reverse reference the variable name to the linked index between the variable lists), we'll have to manually wrap the callback with another lambda and ignore the three arguments:



          lambda var,        # reserve first pos for variable name
          var_idx, # reserve second pos for variable index
          oper, # reserve third pos for operation
          idx=i: # pass in i by reference for indexing point
          trace_intVar(idx) # only pass in the idx


          You cannot just pass lambda...: trace_intVar(i) as i will be passed by value instead of reference in that case. Trust me, I've made this error before. Therefore we pass another argument idx with its default set to i, which will now be passed by reference.



          If trace_add doesn't work, use trace('w', ...) instead.





          For prosperity, here's the complete implemented solution to your question:



          from tkinter import *
          import pandas as pd

          df = pd.DataFrame({'item': list('abcde'), 'default_vals': [2,6,4,5,1]})

          def input_data(df):
          box = Tk()
          height = str(int(25*(df.shape[0]+2)))
          box.geometry("320x" + height)
          box.title("my box")

          #initialise
          params, checkButtons, intVars, strVars = , , ,
          default_vals = list(df.default_vals)
          itemList = list(df.item)

          def trace_intVar(idx):
          if intVars[idx].get() and not params[idx].get():
          params[idx].insert(0, df.default_vals[idx])

          def trace_strVar(idx):
          if strVars[idx].get():
          if not intVars[idx].get():
          intVars[idx].set(True)
          else:
          intVars[idx].set(False)


          for i,label in enumerate(itemList):
          Label(box, text = label).grid(row = i, sticky = W)
          strVars.append(StringVar())
          strVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_strVar(idx))
          params.append(Entry(box, textvariable=strVars[-1]))
          params[-1].grid(row = i, column = 1)
          #params[-1].insert(i, default_vals[i]) # <-- You don't need this any more
          intVars.append(IntVar())
          intVars[-1].trace_add('write', lambda var, var_idx, oper, idx=i: trace_intVar(idx))
          checkButtons.append(Checkbutton(variable = intVars[-1]))
          checkButtons[-1].grid(row = i, column = 3)



          def sumbit(event=None):
          global fields, checked
          fields = [params[i].get() for i in range(len(params))]
          checked = [intVars[i].get() for i in range(len(intVars))]
          box.destroy()

          #add submit button
          box.bind('<Return>', sumbit)
          Button(box, text = "submit",
          command = sumbit).grid(row = df.shape[0]+3, sticky = W)
          box.focus_force()
          mainloop()

          return fields, checked






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 21 '18 at 19:11

























          answered Nov 21 '18 at 17:55









          IdlehandsIdlehands

          6,1551923




          6,1551923













          • Thank you very much for your response! I will go through it step by step to understand what you're doing but on a first note, copying, pasting and running the complete implementation you're providing in the end produces the following error:AttributeError: 'StringVar' object has no attribute 'trace_add' Would you care to double check please?

            – Tony
            Nov 21 '18 at 18:46













          • What version of Python/Tkinter are you using? It works on mine (Python 3.7.1). Are you on Python 2.7?

            – Idlehands
            Nov 21 '18 at 18:50













          • Try trace('w', ...) instead of trace_add('write', ...) and see if it works? Change it for both the strVars and intVars.

            – Idlehands
            Nov 21 '18 at 18:52













          • yes it worked like magic! thank you. I will go through the code now to see if I get it. I have Python 3.5, not sure about tkinter as .__version__ isn't available. Was it a tkinter version issue then? Based on what did you change trace_add('write',..) to trace('w'..) ? Thanks again

            – Tony
            Nov 21 '18 at 18:58






          • 1





            I forgot where I came across trace_add and can't find it at the moment, but I believe it's to replace trace in future versions (perhaps I got a deprecation warning somewhere). Here's the relevant tkinter documentation on trace, and here's a relevant bug report on the proposal to add the various trace_... methods.

            – Idlehands
            Nov 21 '18 at 19:10





















          • Thank you very much for your response! I will go through it step by step to understand what you're doing but on a first note, copying, pasting and running the complete implementation you're providing in the end produces the following error:AttributeError: 'StringVar' object has no attribute 'trace_add' Would you care to double check please?

            – Tony
            Nov 21 '18 at 18:46













          • What version of Python/Tkinter are you using? It works on mine (Python 3.7.1). Are you on Python 2.7?

            – Idlehands
            Nov 21 '18 at 18:50













          • Try trace('w', ...) instead of trace_add('write', ...) and see if it works? Change it for both the strVars and intVars.

            – Idlehands
            Nov 21 '18 at 18:52













          • yes it worked like magic! thank you. I will go through the code now to see if I get it. I have Python 3.5, not sure about tkinter as .__version__ isn't available. Was it a tkinter version issue then? Based on what did you change trace_add('write',..) to trace('w'..) ? Thanks again

            – Tony
            Nov 21 '18 at 18:58






          • 1





            I forgot where I came across trace_add and can't find it at the moment, but I believe it's to replace trace in future versions (perhaps I got a deprecation warning somewhere). Here's the relevant tkinter documentation on trace, and here's a relevant bug report on the proposal to add the various trace_... methods.

            – Idlehands
            Nov 21 '18 at 19:10



















          Thank you very much for your response! I will go through it step by step to understand what you're doing but on a first note, copying, pasting and running the complete implementation you're providing in the end produces the following error:AttributeError: 'StringVar' object has no attribute 'trace_add' Would you care to double check please?

          – Tony
          Nov 21 '18 at 18:46







          Thank you very much for your response! I will go through it step by step to understand what you're doing but on a first note, copying, pasting and running the complete implementation you're providing in the end produces the following error:AttributeError: 'StringVar' object has no attribute 'trace_add' Would you care to double check please?

          – Tony
          Nov 21 '18 at 18:46















          What version of Python/Tkinter are you using? It works on mine (Python 3.7.1). Are you on Python 2.7?

          – Idlehands
          Nov 21 '18 at 18:50







          What version of Python/Tkinter are you using? It works on mine (Python 3.7.1). Are you on Python 2.7?

          – Idlehands
          Nov 21 '18 at 18:50















          Try trace('w', ...) instead of trace_add('write', ...) and see if it works? Change it for both the strVars and intVars.

          – Idlehands
          Nov 21 '18 at 18:52







          Try trace('w', ...) instead of trace_add('write', ...) and see if it works? Change it for both the strVars and intVars.

          – Idlehands
          Nov 21 '18 at 18:52















          yes it worked like magic! thank you. I will go through the code now to see if I get it. I have Python 3.5, not sure about tkinter as .__version__ isn't available. Was it a tkinter version issue then? Based on what did you change trace_add('write',..) to trace('w'..) ? Thanks again

          – Tony
          Nov 21 '18 at 18:58





          yes it worked like magic! thank you. I will go through the code now to see if I get it. I have Python 3.5, not sure about tkinter as .__version__ isn't available. Was it a tkinter version issue then? Based on what did you change trace_add('write',..) to trace('w'..) ? Thanks again

          – Tony
          Nov 21 '18 at 18:58




          1




          1





          I forgot where I came across trace_add and can't find it at the moment, but I believe it's to replace trace in future versions (perhaps I got a deprecation warning somewhere). Here's the relevant tkinter documentation on trace, and here's a relevant bug report on the proposal to add the various trace_... methods.

          – Idlehands
          Nov 21 '18 at 19:10







          I forgot where I came across trace_add and can't find it at the moment, but I believe it's to replace trace in future versions (perhaps I got a deprecation warning somewhere). Here's the relevant tkinter documentation on trace, and here's a relevant bug report on the proposal to add the various trace_... methods.

          – Idlehands
          Nov 21 '18 at 19:10






















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53416804%2fcommunication-between-checkbutton-and-entrybox%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          How to send String Array data to Server using php in android

          Title Spacing in Bjornstrup Chapter, Removing Chapter Number From Contents

          Is anime1.com a legal site for watching anime?