Код, который не проходит, проверяет переменную на функционирование

Я пытаюсь передать список переменных с именем checks функции installFunc, и по какой-то причине она не работает, вот что я (думаю) соответствующий код:

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()

Теперь я пробовал несколько разных вещей - часть лямбы, которую я фактически получил от переполнения стека - у меня небольшие проблемы с пониманием того, как это работает.

Но большая проблема, с которой я сталкиваюсь, заключается в том, почему проверки не передаются в installFunc()? Я хочу, чтобы полный список проверок (столько элементов, сколько я вложил в него) передавался в installFunc().


person Steven Matthews    schedule 24.05.2012    source источник
comment
Могу поспорить, что это как-то связано с тем фактом, что ваша лямбда на самом деле не принимает значение «проверки», поэтому вы получаете какой-то пустой мусор вместо того, что вы ожидаете, но я бы хотел, чтобы кто-то со знанием tk комментарий первый.   -  person Silas Ray    schedule 24.05.2012
comment
Это выглядит маловероятным. Как бы вы это исправить? Возможно, lamvar = lamba : installFunc (проверяет), а затем использовать lamvar внутри кнопки? Будет ли это иметь значение? Я думаю, что это обеспечит выполнение вычислений, чего, если я вас правильно понимаю, сейчас не происходит.   -  person Steven Matthews    schedule 24.05.2012
comment
Я думаю, что я бы разложил этот код совершенно по-другому - создайте класс ProgramInstaller и создайте его экземпляр с соответствующими значениями данных для каждой из ваших устанавливаемых программ.   -  person Hugh Bothwell    schedule 24.05.2012
comment
Я говорю, что я ожидал бы lambda checks : installFunc(checks) или что-то в этом роде.   -  person Silas Ray    schedule 24.05.2012
comment
Выкинул ошибку. TypeError: ‹lambda›() принимает ровно 1 аргумент (дан 0)   -  person Steven Matthews    schedule 24.05.2012
comment
Ну да, это вызовет ошибку, если вы не сможете передать значение лямбде... поэтому я сказал, что вам нужен кто-то, кто работал с Tk.   -  person Silas Ray    schedule 24.05.2012
comment
Может быть, это: lambda c=checks: installFunc(c), хотя я не уверен, что должен делать ваш код... Должен ли checks измениться?   -  person jadkik94    schedule 24.05.2012
comment
Проверяет изменения, нажимая флажок внутри виджета Tk. Изначально все они установлены на 0 (не проверено), но при проверке должно переключиться на 1 (установлено) и если затем нажать кнопку установки, скрипт с их разделом запустится и установит дополнительные части программного обеспечения.   -  person Steven Matthews    schedule 24.05.2012
comment
Я думаю, как вы это сделали, checks не изменится. Вы должны передать variables и манипулировать этим. я опубликую ответ   -  person jadkik94    schedule 24.05.2012


Ответы (3)


variable.get() возвращает значение экземпляра IntVar в момент его вызова, то есть еще до запуска приложения. Таким образом, он будет заполнен нулями.

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()

Кроме того, вам нужно передать variables в качестве аргумента по умолчанию для лямбды, чтобы у вас не было конфликтов с глобальными и локальными переменными.

person jadkik94    schedule 24.05.2012
comment
Похоже, это сработало. Хотя я понятия не имею, что именно здесь происходит. Я должен буду прочитать об этом. - person Steven Matthews; 24.05.2012
comment
@AndrewAlexander, как упоминал jadkik94, когда вы вызываете метод .get() для экземпляра IntVar, он возвращает копию значения, хранящегося в IntVar(), и эта копия не изменяется. Так как ваш код изначально имел его, ваш список проверок будет создан как [0, 0, 0, 0, ...]. Что вам, вероятно, действительно нужно, так это ссылка на объекты IntVar(), что и делает код jadkik94. - person parselmouth; 24.05.2012
comment
Попробуйте print variables[0].get(). Он вернет int. Как вы хотите, чтобы это изменилось? Вам нужно передать variables[0] для вызова функции get в тот момент, когда вы хотите получить значение 0 или 1. Это базовая манипуляция функциями, не имеющая ничего общего с Tkinter. - person jadkik94; 24.05.2012
comment
@AndrewAlexander См. этот вопрос для последнего предложения о лямбда-выражениях. - person jadkik94; 24.05.2012
comment
Если вы действительно хотите использовать аргумент по умолчанию для сохранения variables для лямбда-функции, вам нужно изменить его либо на lambda variables=variables:installCommand(variables), либо на lambda v=variables:installCommand(v) — как есть, вы привязываете переменную v, но затем не используете ее, и вы можно было бы просто сказать lambda: installCommand(variables). - person Edward Loper; 24.05.2012
comment
@ЭдвардЛопер Верно. Вместо этого я хотел использовать v. Спасибо. - person jadkik94; 24.05.2012

Согласно моему предыдущему комментарию, рефакторинговая версия:

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()
person Hugh Bothwell    schedule 24.05.2012

Вы можете сделать это, по крайней мере, несколькими способами. Лично я бы абстрагировал установщик в объект, как упоминалось в комментарии Хью Ботвелла. Это имеет наибольшую гибкость, а также краткое сдерживание состояния. Однако, если вы настроены на использование только одной функции, вы можете использовать functools для «каррирования» функции: динамического создания новой функции, которая включает заданный параметр. Вот изменения, которые вам нужно сделать...

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()

Проблема здесь в том, что variable.get() создает неизменяемый int, и ваш список «checks» никогда не изменится. То, что вы, вероятно, действительно хотите, это...

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()
person parselmouth    schedule 24.05.2012
comment
Я почти уверен, что checks = [variable for variable in variables] бесполезен :P - person jadkik94; 24.05.2012
comment
...размышляет... да... это лишнее, не так ли. :) - person parselmouth; 24.05.2012