0

I am trying to pass along a variable list named checks to the function installFunc, and for some reason it doesn't seem to be working, here's what I (think is) the relevant code:

def installFunc(checks):
    subprocess.call("md c:\MGInstall", shell=True)
    subprocess.call (u"net use w: \\it01\files")
    if checks[0] == 1:
        subprocess.call(u"w:\\software\\snagitup.exe")
    if checks[1] == 1:
        subprocess.call(u"w:\\software\\camtasia.exe")
    if checks[2] == 1:
        urllib.urlretrieve(u"LONGURL", u"c:\\MGinstall\\gotomeeting.exe")
        subprocess.call (u"c:\\MGinstall\\gotomeeting.exe")
    if checks[3] == 1:
        sixtyfourcheck()
        if is64bit == True:
            urllib.urlretrieve(u"LONGURL", u"c:\\MGinstall\\tortoiseSVN.exe")
        elif is64bit == False:
            urllib.urlretrieve(u"LONGURL", u"c:\\MGinstall\\tortoiseSVN.exe")
    #urllib.urlretrieve(u"LONGURL", u"c:\\MGinstall\\MGinstall.exe")
    #subprocess.call (u"c:\\MGinstall\\MGinstall.exe")
    #subprocess.call (u"w:\\printers\\installer\\printer.exe")

app = Tk()

w = Label(app, text="IT Automatic Installer")
w.pack()

text = ["Snagit", "Camtasia", "GotoMeeting", "TortoiseSVN"]
variables = []
for name in text:
    variables.append(IntVar())
    Checkbutton(text=name, variable=variables[-1]).pack()

checks = [variable.get() for variable in variables]
b = Button(text="Install", command= lambda : installFunc(checks))
b.pack()

app.mainloop()

Now, I've tried a few different things - the lamba portion I was actually given by stack overflow - I am having a little trouble understanding how it works.

But the big problem I am having is - why isn't checks being passed to installFunc()? I want to have the full list of checks (as many items as I put into it) passed to installFunc().

Silas Ray
  • 25,682
  • 5
  • 48
  • 63
Steven Matthews
  • 9,705
  • 45
  • 126
  • 232
  • My bet is that it has something to do with the fact that your lambda is not actually taking in a 'checks' value, so you are getting some empty junk instead of what you expect, but I'd want someone with knowledge of tk to comment first. – Silas Ray May 24 '12 at 17:27
  • That's looking unlikely. How would you fix it? Perhaps lamvar = lamba : installFunc(checks), and then use lamvar inside the button? Would that make a difference? I'm thinking that would ensure that computation goes through, which if I'm reading you correctly, you feel isn't happening right now. – Steven Matthews May 24 '12 at 17:37
  • 1
    I think I would factor this code completely differently - make an ProgramInstaller class and instantiate it with appropriate data values for each of your programs to be installed. – Hugh Bothwell May 24 '12 at 17:40
  • What I'm saying is that I would expect `lambda checks : installFunc(checks)`, or something to that effect. – Silas Ray May 24 '12 at 17:41
  • Threw up an error. TypeError: () takes exactly 1 argument (0 given) – Steven Matthews May 24 '12 at 17:52
  • Well, yes, that is going to cause an error unless you are able to pass a value to the lambda... hence why I said you need someone who has worked with Tk. – Silas Ray May 24 '12 at 17:56
  • Maybe that: `lambda c=checks: installFunc(c)`, although I'm not sure what your code should be doing... Is `checks` supposed to change? – jadkik94 May 24 '12 at 17:57
  • Checks changes based on clicking a checkbox inside the Tk widget. Initially, they are all set to 0 (unchecked), but upon checking should switch to 1 (checked) and if the install button is then clicked, the script with their section will run and install additional pieces of software. – Steven Matthews May 24 '12 at 18:03
  • I think as you did it `checks` won't change. You should pass `variables`, and manipulate this one. I'll post an answer – jadkik94 May 24 '12 at 18:07

3 Answers3

3

variable.get() returns the value of the IntVar instance at the moment it was called, which is before the application even starts. So, it will be full of zeros.

def installCommand(variables):
    checks = [variable.get() for variable in variables]
    return installFunc(checks)

b = Button(text="Install", command= lambda v=variables: installCommand(v))
b.pack()

Also, you need to pass variables as a default argument to the lambda, so that you don't have conflicts with global and local variables.

jadkik94
  • 7,000
  • 2
  • 30
  • 39
  • This appears to have worked. I have no idea what's going on here though, exactly. I'll have to read up on it. – Steven Matthews May 24 '12 at 18:22
  • @AndrewAlexander as jadkik94 mentioned, when you call the .get( ) method on an instance of IntVar, it returns a copy of the value held by the IntVar( ) and this copy doesn't change. So as your code originally had it, your checks list will be created like [0, 0, 0, 0, ...]. What you probably really want is a reference to the IntVar( ) objects, which jadkik94's code accomplishes. – parselmouth May 24 '12 at 18:26
  • Try `print variables[0].get()`. It will return an int. How do you want that to change? You need to pass `variables[0]` to call the `get` function at the moment you want to get the value 0 or 1. It is basic function manipulation, nothing to do with Tkinter. – jadkik94 May 24 '12 at 18:27
  • @AndrewAlexander See [this question](http://stackoverflow.com/questions/7514093/lambda-function-dont-closure-the-parameter-in-python) for the last sentence about lambdas. – jadkik94 May 24 '12 at 18:31
  • If you really mean to be using a default arg to save `variables` for the lambda function, then you need to change that to either `lambda variables=variables:installCommand(variables)` or `lambda v=variables:installCommand(v)` -- as it is, you're binding the variable `v` but then not using it, and you might as well just say `lambda: installCommand(variables)`. – Edward Loper May 24 '12 at 18:39
  • @EdwardLoper True. I meant to use `v` instead. Thanks. – jadkik94 May 24 '12 at 18:41
0

You could do this in a couple of ways, at least. Personally, I would abstract the installer into an object, as mentioned by Hugh Bothwell's comment. This has the most flexibility as well as a concise containment of state. However, if you're set on using just a single function, you can use the functools to "curry" the function: dynamically creating a new function which embeds the given parameter. Here's the changes you'll need to do...

def installFunc(checks):
    subprocess.call("md c:\MGInstall", shell=True)
    subprocess.call (u"net use w: \\it01\files")
    if checks[0].get( ) == 1:
        subprocess.call(u"w:\\software\\snagitup.exe")
    if checks[1].get( ) == 1:
        subprocess.call(u"w:\\software\\camtasia.exe")
    if checks[2].get( ) == 1:
        urllib.urlretrieve(u"LONGURL", u"c:\\MGinstall\\gotomeeting.exe")
        subprocess.call (u"c:\\MGinstall\\gotomeeting.exe")
    if checks[3].get( ) == 1:
        sixtyfourcheck()
        if is64bit == True:
            urllib.urlretrieve(u"LONGURL", u"c:\\MGinstall\\tortoiseSVN.exe")
        elif is64bit == False:
            urllib.urlretrieve(u"LONGURL", u"c:\\MGinstall\\tortoiseSVN.exe")


import functools

app = Tk()

w = Label(app, text="IT Automatic Installer")
w.pack()

text = ["Snagit", "Camtasia", "GotoMeeting", "TortoiseSVN"]
variables = []
for name in text:
    variables.append(IntVar())
    Checkbutton(text=name, variable=variables[-1]).pack()

checks = [variable.get() for variable in variables]

#-- Here's where we "curry" the installFunc, producing the new doInstall function.
doInstall = functools.partial(installFunc, checks)

b = Button(text="Install", command = doInstall)
b.pack()

app.mainloop()

The problem here is that variable.get( ) creates an immutable int, and your list "checks" will never change. What you probably really want is then...

import functools

app = Tk()

w = Label(app, text="IT Automatic Installer")
w.pack()

text = ["Snagit", "Camtasia", "GotoMeeting", "TortoiseSVN"]
variables = []
for name in text:
    variables.append(IntVar())
    Checkbutton(text=name, variable=variables[-1]).pack()

checks = [variable for variable in variables]

#-- Here's where we "curry" the installFunc, producing the new doInstall function.
doInstall = functools.partial(installFunc, checks)

b = Button(text="Install", command = doInstall)
b.pack()

app.mainloop()
BenMorel
  • 34,448
  • 50
  • 182
  • 322
parselmouth
  • 1,598
  • 8
  • 8
0

Per my previous comment, a refactored version:

import urllib
import subprocess
import os
import Tkinter as tk

class ProgramInstaller(object):
    def __init__(self, name, descr, do_install):
        self.name    = name
        self.descr   = descr
        self.do_install = do_install    # can be function or list of strings

    def install(self):
        if callable(self.do_install):
            self.do_install()
        else:
            for s in self.do_install:
                subprocess.call(s)

TEMP_DIR = r"c:\MGInstall"

def makeTempDir(dir=TEMP_DIR):
    # need to expand on this - what if dir already exists, etc
    os.mkdir(dir)

def removeTempDir(dir=TEMP_DIR):
    # need to expand on this - del files in dir before rmdir, etc
    os.rmdir(dir)

def installGoToMeeting():
    makeTempDir()
    url = "http://www.gotomeeting.com/download/something"
    fname = os.path.join(TEMP_DIR, "gotomeeting.exe")
    urllib.urlretrieve(url, fname)
    subprocess.call(fname)
    removeTempDir()

def installTortoiseSVN():
    makeTempDir()
    if is64bit(): url = "http://www.tortoisesvn.net/download/something/64bit"
    else:         url = "http://www.tortoisesvn.net/download/something/32bit"
    fname = os.join(TEMP_DIR, "tortoisesvn.exe")
    urllib.urlretrieve(url, fname)
    subprocess.call(fname)
    removeTempDir()

installers = (
    ProgramInstaller("SnagIt",      "Take screen-shots",           [r"net use w: \\it01\files", r"w:\software\snagitup.exe"]),
    ProgramInstaller("Camtasia",    "Record your desktop",         [r"net use w: \\it01\files", r"w:\software\camtasia.exe"]),
    ProgramInstaller("GoToMeeting", "Web conferencing",            installGoToMeeting),
    ProgramInstaller("TortoiseSVN", "(Sub)Version control client", installTortoiseSVN),
    ProgramInstaller("Printer",     "HP4020 Printer drivers",      [r"net use w: \\it01\files", r"w:\printers\installer\printer.exe"])
)

def doInstall():        # install-button callback
    for inst in installers:
        if inst.cbvar.get():
            inst.install()

def main():
    app = tk.Tk()
    tk.Label(app, text="IT Automatic Installer").pack()

    # need to fiddle with a grid layout to make this look right
    for inst in installers:
        inst.cbvar = tk.IntVar()
        tk.Checkbutton(text=inst.name, variable=inst.cbvar).pack()
        tk.Label(text=inst.descr).pack()

    tk.Button(text="Install", command=doInstall()).pack()
    app.mainloop()

if __name__=="__main__":
    main()
Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99