首页 > 解决方案 > 为什么当它作为一个函数被隔离时,短 Python 脚本的运行方式会有所不同?

问题描述

作为在特定 Web 界面上点击某些点的程序的一部分,该组件使用户能够单击三个点并编写一个配置文件,该文件指示程序每次运行时将光标精确放置在哪里。这些行在程序主体中时按预期工作(Python 3.6);问题是,我想将它们隔离为一个函数,这样我就可以让用户选择是否重新配置。(如果用户没有切换浏览器也没有调整大小,现有的配置文件就可以了。)

我希望知识渊博的 Pythonista 可以帮助我理解为什么简单地缩进这些以window=Tk()四个/八个空格开头的行并将它们放在一个函数中会导致它们无法正常运行,而是在第一次调用的中间挂断到 on_click。(编辑:来自终端的错误消息在脚本下方)

import threading, pynput, os, configparser
from pynput.mouse import Listener
from tkinter import *
configfile_name = "config.ini"
cfg = configparser.ConfigParser()

def write_config(x_coord, y_coord):
   global configfile_name
   c = open(configfile_name, "a", encoding="utf-8")
   c.write('x=' + x_coord + '\r')
   c.write('y=' + y_coord)
   c.write('\r\n')
   c.close()

def write_first_section(section_name):
   global configfile_name
   c = open(configfile_name, "w", encoding="utf-8")
   c.write('[' + section_name + '] \r')
   c.close()

def write_section(section_name):
   global configfile_name
   c = open(configfile_name, "a", encoding="utf-8")
   c.write('[' + section_name + '] \r')
   c.close()

def on_click(x, y, button, pressed) :
   global count, window, listener
   write_config(str(x), str(y))
   window.after(2000, window.destroy)    
   listener.stop()  

window = Tk()
window.title("Map your screen")
window.geometry('350x80+300+225')
window.lift()
write_first_section('tab')
lbl = Label(window, text="With the QP chat interface screen up,\nclick the 'New' tab at upper left above the blue bar")
lbl.grid(column=0, row=0)
with Listener(on_click=on_click) as listener:
   window.mainloop()
   listener.join()
listener.stop()
write_section('pickup')
window = Tk()
window.title("Step two!")
window.geometry('350x80+300+225')
window.lift()
lbl = Label(window, text="Now, click just below the blue bar")
lbl.grid(column=0, row=0)
with Listener(on_click=on_click) as listener:
   window.mainloop()
   listener.join()
listener.stop()
write_section('paste')
window = Tk()
window.title("Step three!")
window.geometry('350x80+300+225')
window.lift()
lbl = Label(window, text="Lastly, click in the text box")
lbl.grid(column=0, row=0)
with Listener(on_click=on_click) as listener:
   window.mainloop()

当放置在函数中时,脚本会抛出以下错误:

Unhandled exception in listener callback
Traceback (most recent call last):
  File "/home/bruce/.local/lib/python3.6/site-packages/pynput/_util/__init__.py", line 157, in inner
    return f(self, *args, **kwargs)
  File "/home/bruce/.local/lib/python3.6/site-packages/pynput/_util/xorg.py", line 458, in _handler
    self._handle(self._display_stop, event)
  File "/home/bruce/.local/lib/python3.6/site-packages/pynput/mouse/_xorg.py", line 141, in _handle
    self.on_click(px, py, self._button(event.detail), True)
  File "/home/bruce/.local/lib/python3.6/site-packages/pynput/_util/__init__.py", line 78, in inner
    if f(*args) is False:
  File "does_it_all.py", line 37, in on_click
    window.after(2000, window.destroy)
NameError: name 'window' is not defined
Traceback (most recent call last):
  File "does_it_all.py", line 231, in <module>
    map()
  File "does_it_all.py", line 192, in map
    listener.join()
  File "/home/bruce/.local/lib/python3.6/site-packages/pynput/_util/__init__.py", line 205, in join
    six.reraise(exc_type, exc_value, exc_traceback)
  File "/home/bruce/.local/lib/python3.6/site-packages/six.py", line 692, in reraise
    raise value.with_traceback(tb)
  File "/home/bruce/.local/lib/python3.6/site-packages/pynput/_util/__init__.py", line 157, in inner
    return f(self, *args, **kwargs)
  File "/home/bruce/.local/lib/python3.6/site-packages/pynput/_util/xorg.py", line 458, in _handler
    self._handle(self._display_stop, event)
  File "/home/bruce/.local/lib/python3.6/site-packages/pynput/mouse/_xorg.py", line 141, in _handle
    self.on_click(px, py, self._button(event.detail), True)
  File "/home/bruce/.local/lib/python3.6/site-packages/pynput/_util/__init__.py", line 78, in inner
    if f(*args) is False:
  File "does_it_all.py", line 37, in on_click
    window.after(2000, window.destroy)    
NameError: name 'window' is not defined

当我尝试将 on_click(....) 函数定义之后的所有内容放入名为 map() 的函数中,然后尝试运行 map() 时,它在将 [tab] 写入 config.ini 文件后挂断,留下第一个窗口卡在屏幕上。但是当我按照上面写的方式运行它时,它会正确地通过所有三个窗口并写入配置文件的所有三个部分。我确信脚本中存在可怕的错误,但是:为什么这 32 行在公开时运行良好,但在它们组成函数时会中断?感谢您的任何帮助和建设性的责骂!

标签: pythontkinter

解决方案


当您将这些行移到一个函数中时,您定义的window(and listener) 变量不再是全局变量,而是成为该函数的本地变量。您的on_click函数依赖于window(and listener) 来全局定义,并且由于它们不再作为全局变量存在,因此您的代码损坏了。

最小的修复是添加:

global window, listener

到新函数的顶部,因此它将windowlistener视为全局变量,而不是将它们视为局部变量的默认行为。

一个更彻底的解决方案是编写一个类,其中windowlistener(可能还有其他东西)是实例属性,通过 访问self,使用您的新函数和on_click该类的方法。在这种情况下,这并不重要,但任何时候你发现自己依赖于global可变的全局状态,这通常是一个糟糕的设计,你首先真的想要一个类。


推荐阅读