Hello I'm trying to make a program where it asks for a python module, get its functions, append them into a list, and put the list values as options for a OptionMenu. I have the following code:
def fetch_module():
global get_module
global functionmodulename
get_module = askopenfilename(title="get module", filetypes=[("Python source","*.py")])
functionmodulename = inspect.getmodulename(get_function)
getfunctions(sys.modules[get_module])
def fetch_function(module)
global l
l = []
for key, value in module.__dict__.items():
if type(value) is FunctionType:
l.append(value)
if __name__ == "__main__":
function_name = StringVar
OptionMenu(function_name, l).pack()
fetch_module()
mainloop()
However, if I run this, a "[]"show up as the only choice. can I fix this?
There are several things to fix in your code. First of all, you need to create the Tkinter app, let's call it master.
Right now you are assigning the class StringVar to the variable function_name. That does not work. You need to create a new StringVar instance.
Putting a list of things in an OptionMenu is also tricky. see: http://effbot.org/tkinterbook/optionmenu.htm
import Tkinter
# or depending on your python version: import tkinter
if __name__ == "__main__":
master = Tkinter.Tk()
sv = Tkinter.StringVar(master)
l = ['a','b','b','c']
apply( Tkinter.OptionMenu, (master, sv) + tuple(l)).pack()
Tkinter.mainloop()
Related
I was looking over a bit of code rooted in urwid:
import urwid
from functools import partial
from random import randint
class State(object):
def __init__(self, main_widget):
self.main_widget = main_widget
def handle_keystroke(app_state, key):
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
else:
loop.widget = urwid.Filler(urwid.Button('new rand int:' + str(randint(0, 100))))
app_state = State(urwid.Filler(urwid.Button('original widget')))
callback = partial(handle_keystroke, app_state)
loop = urwid.MainLoop(app_state.main_widget, unhandled_input=callback)
loop.run()
and noticed that loop is referenced in the function unhandled_input before it's defined. Furthermore, it's not passed as a parameter, it's just hard coded into the function by name. 1) Why is this possible, and: 2) is there a clearer alternative? It is difficult to do otherwise, as there is a circular dependencies of loop, app_state and callback.
I'm not sure how much of your sample code represents the original code, but it looks like you may want to get familiar with the technique of using urwid's custom widgets wrapping text widgets, as shown in the answer with an example widget that displays a text content one line at the time.
Here is an example of writing something similar to the sample code you provided, in a design that fits urwid and Python a bit better:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function, absolute_import, division
import urwid
from random import randint
class RandomNumberWidget(urwid.WidgetWrap):
def __init__(self):
self.random_number = None
self.text_widget = urwid.Text(u'')
super(RandomNumberWidget, self).__init__(self.text_widget)
def roll(self):
self.random_number = randint(0, 100)
self.update()
def update(self):
"""Update UI
"""
if self.random_number is None:
self.text_widget.set_text('No number set')
else:
self.text_widget.set_text('Random number: %s' % self.random_number)
class App(object):
def __init__(self):
self.random_number_widget = RandomNumberWidget()
top_message = 'Press any key to get a random number, or q to quit\n\n\n'
widget = urwid.Pile([
urwid.Padding(urwid.Text(top_message),
'center', width=('relative', len(top_message))),
self.random_number_widget,
])
self.widget = urwid.Filler(widget, 'top')
def play(self):
self.random_number_widget.roll()
def play_or_exit(self, key):
if key in ('q', 'Q', 'esc'):
raise urwid.ExitMainLoop()
app.play()
if __name__ == '__main__':
app = App()
loop = urwid.MainLoop(app.widget, unhandled_input=app.play_or_exit)
loop.run()
Depending also on what you actually want to do, it could make sense to make the custom widgets respond to the keyboard events, instead of doing it all in the global handler (which is totally fine for simple programs, IMO).
When python compiles a function, left-hand-side variables that are the target of assignment are treated as local and the rest are global. loop is not assigned, so when python runs loop.widget = urwid.Filler(...), it knows that loop is not a local variable and it will look the name up in the module's namespace.
Module namespaces are dynamic, so as long as loop = urwid.MainLoop(app_state.main_widget, unhandled_input=callback) runs before the lookup, loop is created and it works. Since the callback can't be executed until loop.run(), loop will be defined.
This is one of the classic risks of singletons and global state. Its not always easy to make sure the resource is created before it is used.
I have a button whose function is
def callback2():
callback()
The callback() function is
def callback():
usein = None
if inspect.stack()[1][3] == callback2:
global inputText
usein = inputText.get()
return None
while True: #freezes everything, because tkinter
if usein:
return usein
Now, the reason I have to do it like this is because other functions call callback() looking for the value inputted by the button, but I have to make them wait for the button to be pressed. But since I'm using tkinter, the while loop doesn't work - it just makes the GUI freeze. So what can I use instead? I've been working on this for days. I'd be glad to add any other parts of my code if needed.
isButtonClicked = false #a global variable
def callback2():
isButtonClicked = true
callback()
isButtonClicked = false
One idea may be to use a global variable called isButtonClicked and assign a false value, and modify the other methods which call callback method like this:
def othermethod():
if isButtonClicked:
callback()
But you've to make sure that the variables are thread-safe.
Not a tkinter expert, but if you want to get some text input on a button click, the following may work.
def callback():
usein = entry.get()
# do whatever with usein
master = Tk()
entry = Entry(master) # the text input
Button(master, text='Button', command=callback)
I wrote this code to be a version manager, but it doesn't execute the command changeDir(). Why?
https://pastebin.com/VSnhzRzF
You forgot to pass a 'name' argument to changeDir function. And there's no exception because your statement has no effect!
Snippet to represent the problem:
import sys
def exec_smth():
# execution without effect
exec('write_smth')
try:
# execution with exception because of missing argument
exec('write_smth()')
except TypeError as error:
# now we pass an argument
exec('write_smth("I failed because of %s" % error )')
def write_smth(smth):
sys.stdout.write(smth)
exec_smth()
Anyway, outside your __init__ function there're no StringVars at all thanks to garbage collector, so your code would fail anyway!
There're even more problems, because you never bind any of your sv{} to a widget and expect something in return! But ok, let's try to do things with exec:
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.entries = []
for _ in range(5):
exec('self.sv{} = tk.StringVar()'.format(_))
exec('self.sv{}.trace("w", self.change_sv)'.format(_))
exec('self.entries.append(tk.Entry(self, text="", textvariable=self.sv{}))'.format(_))
for entry in self.entries:
entry.pack()
def change_sv(*args):
# get id of a variable (you can't rely on that (0-9)!)
idx = args[1][-1:]
# get new value
value = getattr(args[0], 'sv{}'.format(idx)).get()
# result
print('Value changed in self.sv%s to %s!' % (idx, value))
app = App()
app.mainloop()
Output:
As you see - we always need a reference to StringVars and I think that option with a list of them is far better!
Note: If you need to pass something to callback function - use a lambda function! All code tested with Python 3.
Links:
The Variable Classes
Tkinter Callbacks
Behavior of exec function in Python 2 and Python 3
I have a question when it comes to OOP in general, as well as Python in particular. Let's say that I have, for instance, priorities.py - a simple GUI program to manage priorities and there are three classes: Priority, Client, GuiPart:
# priorities.py
# GUI program to manage priorities
from tkinter import *
class Priority:
pass
class GuiPart:
def __init__(self):
self.root = self.createWindow()
def createWindow(self):
root = Tk()
root.resizable(width = False, height = False)
root.title("Priorities")
return root
def display(self):
Label(self.root,
text = "testes").grid(row = 0, column = 1)
class Client:
pass
def main():
g = GuiPart()
g.display()
root = g.root.mainloop()
main()
Should I put def main() outside of any classes, or should I put it in Client class?
Every module(python file) have a builtin __name__ variable, if this equal to "__main__" this means that this file ran directly, but if __name__ is equal to other things this means that current file imported to other python files.
if you running this file directly or as module, you can use __name__ variable to recognize type of code-file used, similar below:
# Some codes
if __name__ == '__main__':
main()
Now users can running this file directly and/or programmers can use this module in other codes without running main() function.
My preferred approach:
separate main file with the if __name__ == '__main__': directive
Reasons:
Application Logic and calling logic is separate. so you can scale easily
Can maintain and apply different environment settings effectively. so, we can seamlessly transition between dev/test/stage/prod setup
Increases code readability as well
I'm trying to carry a change from a global variable over to another module in Python2.7.
I've done this in similar situations before but for some reason it won't work in this instance.
The first file is the one that runs the program. It sets up a global variable and changes it according to the option selected. I've pasted a bit of the code below.
runnerMod:
import Tkinter
from main_mod import*
choice = "0"
def main():
main_mod=functOne()
class GUI(Tkinter.Tk):
def __init__(self, parent):
Tkinter.Tk.__init__(self, parent)
self.parent = parent
self.initialize()
def initialize(self):
self.grid()
self.update()
btnOption1 = Tkinter.Button(self, text=u"Option 1", command=self.onButtonClick)
btnOption1.grid(column=0, row=1)
def onButtonClick(self):
selection = "1"
self.destroy()
exec 'choice=%s' %selection in globals()
main()
class menuSelection:
def OPTIONCHOSEN(self):
return choice
if __name == "__main__":
app = GUI(None)
app.mainloop
I want the global variable named choice from runnerMod.py to carry over to this module.
main_mod:
from runnerMod import menuSelection
def functOne():
userInput = menuSelection().OPTIONCHOSEN()
print userInput
The global variable choice starts at 0, but I want to change it to 1 in the runnerMod.py module and have this reflected in the main_mod.py module. Since I'm rewriting an interface to an existing program my options are a little limited in the way its coded. Anyone have any ideas here?
As it turns out, I couldn't pass over changes to a global variable from runnerMod.py because it was the module that launched the program. What I had to do was use runnerMod.py to launch the program and then call a function in main_mod.py. This function called BACK to a class in runnerMod.py and loaded up the GUI. Only by calling back and THEN modifying the global variable could I pass over the changes.