使用信号处理函数设置变量值以响应来自GUI的信号的单线程?

时间:2014-02-09 20:00:33

标签: python user-interface pyqt pyside

不,问题不重复。这不是关于“如何在python中说a=1”?

并非琐碎。请仔细阅读。

答案是这是不可能的来创建这样的单线程,至少不使用脏黑客。请参阅接受的答案了解详情。

如果你不想读书,但为了方便业力来到这里,请经过并停止迷惑我,因为接受的答案非常有用。 :)


问题:

我正在做一些GUI编程,并希望创建一个非常短的信号处理程序,它将局部变量设置为一个值,以响应来自GUI的信号。

def function():
    output = False #this is local variable; I want to change its value by signal handler
    button = PyQt4.QtGui.QPushButton("simple button")
    #next line sets output to True
    button.pressed.connect(lambda:SOME_FUNCTION_I_AM_LOOKING_FOR(output, True))
    #Now output==True

你能否建议用SOME_FUNCTION_I_AM_LOOKING_FOR代替什么?显然,你不能只说button.pressed.connect(output=True),因为output=True不是函数。

为了说明我在寻找的东西,这里是一个模拟:如果你想设置一个对象的属性,你可以使用setattr函数。这是上面代码的模拟,设置属性而不是局部变量:

def function():
    ...
    #create a dummy UserObject class and make an instance of it to store attributes
    output_object = type('UserObject', (), {})() 
    output_object.it_was_pressed = False #flags that button was pressed
    #when "pressed" signal of button is emitted, "it_was_pressed" attribute of output_object is set to True
    button = PyQt4.QtGui.QPushButton("simple button")
    button.pressed.connect(lambda: setattr(output_object, "it_was_pressed", True) 

那是我想做什么。我想要做的是创建一个类似物来设置一个局部变量来响应信号:

更新

我要求单线解决方案:简洁优雅。当然,我可以用3行做到这一点,但我不想这样做。

2 个答案:

答案 0 :(得分:2)

没有 可靠的方式“以编程方式”修改本地环境(即没有明确的分配)。

例如,这适用于python2:

>>> def test():
...     output = False
...     exec('output=True')
...     print(output)
... 
>>> test()
True
>>> test()
True

然而, 在python3中无法正常工作!

>>> def test():
...     output = False
...     exec('output=True')
...     print(output)
... 
>>> test()
False

修改locals()返回的字典可能不起作用,例如:

>>> def test():   #python 2 and 3
...     output = False
...     locals()['output'] = True
...     print(output)
... 
>>> test()
False

它可能适用于某些旧版本的python。

exec可能在python2中有效,因为它是一个语句,因此python编译器可以确定该函数是否需要真正使用dict来存储其变量并使exec工作properly`。检查这个很简单:

>>> def test():
...     exec('pass')
...     locals()['output'] = True
...     print(output)
... 
>>> test()
True
>>> def test():
...     locals()['output'] = True
...     print(output)
... 
>>> test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in test
NameError: global name 'output' is not defined

正如您可以看到函数是否包含exec语句python避免优化局部变量而只使用dict(从而使locals()的更改正常工作)。如果未找到exec,则更改无效。

在python3中exec 一个函数,python编译器总是优化本地查找。


所以:

  • 在python2中你可以用exec做到这一点,但这是一个声明,不能在lambda s
  • 中使用
  • 在python3中,无论你怎么努力,你只是不能做你想做的事。只需 no 方法即可修改本地变量,而无需显式赋值语句。

因此,您要求的是AFAIK,在一行的单个表达式中是不可能的(至少以任何合理的可读方式)。 正确做你想做的事的方法是在python3中使用nonlocal

output = False
def handler():
    nonlocal output
    output = True

button.pressed.connect(handler)

在python2中,你甚至没有nonlocal所以你必须使用其中一个黑客来处理这个问题(比如输入output一个元素列表等。)。

我不排除存在丑陋不可读 hackish 编写定义函数的单行的方式(不是lambda!)而且可以达到你想要的效果,但我非常确定没有“简洁优雅”的单行程可以满足您的需求。


显然,如果你不介意output成为一个清单,你可以做丑陋的事情:

output = [False]

button.pressed.connect(lambda: output.__setitem__(0, True))

答案 1 :(得分:0)

如果你只关心布尔检查,你可以依赖一些Pythonic习语:

output = {} # similar to False case

def callback_func(output, val):
    if val:
       output['set']=True
    else:
       output.clear()

如果字典为空,则将其评估为False,否则为true。