Reputation: 33
I'm creating simple notepad program in Tkinter. I decided to put functions in separate file. Is it possible if the functions are operating on variables declared in main file? This is snippet of the code: main.py
from tkinter import *
from otherfile import cut
root = Tk()
....
menu_edit.add_command(label='Cut', compound='left', command=cut)
...
main_text = Text(root, wrap ='word')
main_text.pack(expand='yes', fill = 'both')
now I have otherfile.py
def cut():
main_text.event_generate('<<Cut>>')
return 'break'
Once I run it I'll get: Exception in Tkinter callback
Traceback (most recent call last):
File "C:...\tkinter\__init__.py", line 1699, in __call__
return self.func(*args)
File "C:\....otherfile.py", line 3, in cut
main_text.event_generate('<<Cut>>')
NameError: name 'main_text' is not defined
So I guess otherfile.py does not understand main_text which is defined in main.py. Is there a way to bypass it and allow me to put all the functions in different py file?
Upvotes: 0
Views: 790
Reputation: 40803
It is a bad practice and tons of headache to use global variables. May I suggest to modify cut()
to take a parameter:
# otherfile.py
def cut(text_control):
text_control.event_generate('<<Cut>>')
return 'break'
Then in the main module, call it as such:
# main.py
menu_edit.add_command(label='Cut', compound='left', command=lambda: cut(main_text))
This way, you don't have to deal with troubles later. Besides, you can now use function cut()
for other text boxes if you want.
Upvotes: 0
Reputation: 1371
cut
is trying to use a global variable from another file. Even if you found a way to get around circular imports, it's a messy way to go about things. It's better to write functions that operate independent of global variables. For one thing, it makes them much easier to modify and test. When you need to deal with assigning command=function
and function
takes variables, functools.partial is your friend.
def cut(tk_text_obj):
tk_text_obj.event_generate('<<Cut>>')
return 'break'
and then in main file, first declare main_text
and then use functools.partial
to create a callable that takes no arguments.
from functools import partial
from tkinter import *
from otherfile import cut
root = Tk()
....
main_text = Text(root, wrap ='word')
cut_main_text = partial(cut, main_text)
menu_edit.add_command(label='Cut', compound='left', command=cut_main_text)
# or just combine the above two lines using command=partial(cut, main_text)
...
main_text.pack(expand='yes', fill = 'both')
Upvotes: 1
Reputation: 11
It is possible. You should import main
in otherfile
or modify otherfile.cut
method to accept main_text as method argument. Second option depends on that if menu_edit.add_command
allows passing arguments to command.
I think you have two problems.
I believe below example is moreover situation you have.
a.py:
import b
commands = []
def add_command(cmd):
commands.append(cmd)
def run_commands():
for cmd in commands:
print cmd()
def local_cmd():
return 'local cmd output'
if __name__ == '__main__':
add_command(local_cmd)
add_command(b.b_cmd)
run_commands()
b.py:
import a
def b_cmd():
l = a.local_cmd()
return 'B %s B' % l
Above snippet works as expected when running with python a.py
.
But when you skip if __name__ == '__main__':
you will observe similar situation. Script fails because when you import a
in b
, add_command(b.b_cmd)
in a
is called, but b
was not imported yet.
Upvotes: 0