Don't want to use global. Python [closed] - python

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 8 years ago.
Improve this question
I have been trying to use a variable which I defined within a function within a another but I don't want to use a Global.
import sys
from tkinter import *
from tkinter import messagebox
from tkinter import colorchooser
from tkinter import filedialog
x = "#000000"
#colour chooser
def mColour():
color = colorchooser.askcolor()
mlabel2 = Label(mGui,text=color).pack()
messagebox.showinfo(title = "Colour",message = "This feature has not been fully added yet.")
x = color[1]
print (color[1])
return x
#printing message
def mhello():
mtext = ment.get()
mlabel2 = Label(mGui,text=mtext, fg = (b)) # color varible = x
mlabel2.pack()
#message
def mNew():
messagebox.showerror(title = "(New) Error",message = "This feature has not been added yet.")
def mAbout():
messagebox.showinfo(title = "About",message = "This Jono's susure personal data base. (Will be)")
#question message
def mQuit():
mExit = messagebox.askokcancel(title = "Quit",message = "Are you sure")
if mExit > 0:
mGui.destroy()
#open
def mOpen():
myopen = filedialog.askopenfile()
mlabel3 = Label(mGui,text=myopen).pack()
messagebox.showinfo(title = "Open",message = "This feature has not been fully added yet.")
mGui = Tk()
ment = StringVar()
mGui.geometry("300x200+100+100")
mGui.title("Jono's Clock")
mlable = Label(mGui,text="My Label",fg = "red").pack()
mbutton = Button(mGui,text ="OK",command = mhello,fg = "red").pack()
mEntry = Entry(mGui,textvariable=ment).pack()
# Menu
menubar = Menu(mGui)
filemenu = Menu(menubar, tearoff = 0)
filemenu.add_command(label = "New",command = mNew)
filemenu.add_command(label = "Open",command = mOpen)
filemenu.add_command(label = "Colour",command = mColour)
filemenu.add_command(label = "About",command = mAbout)
filemenu.add_command(label = "Quit",command = mQuit)
menubar.add_cascade(label = "File",menu = filemenu)
mGui.config(menu = menubar)
mGui.mainloop()
I'm using python 3.3 on windows 7.
I have add the rest of the code as requested.

As the comments suggested, it would be good to pass the variable x as a parameter to the function.
def mColour(x):
...
def mHello(x):
...
Then when you want to call the functions:
x = "#000000"
x = mColour(x)
mhello(x)
As a rule of thumb, defining global variables inside a function is a bad idea (with rare circumstances being exceptions). You want to keep local and global namespaces seperate. Functions allow the passing of arguments for this reason.

#colour chooser
def mColour(x):
color = colorchooser.askcolor()
mlabel2 = Label(mGui,text=color).pack()
messagebox.showinfo(title = "Colour",message = "This feature has not been fully added yet.")
x = color[1]
print (color[1]) # Just a test
return x
x = "#000000" # default text colour
mColour(x)

You could use a class instead as I don't think there's a way to access local variables from another function.
class Example(object):
x="00006"
def c():
print(Example.x)
c()
So here you're not using any globals at all

EDIT: Now your question makes a lot more sense. Because your functions are connected to these UI controls, it becomes much more difficult to pass parameters to them.
As such, you need a structure in which to store these values that you get from the UI. Then, when mhello() needs it, it has access to a function that can get the color from whatever structure you choose to use.
Now, mHello still can't take the color or the structure as a parameter, so you've got three options.
1) You can make something global, which you've already said you don't want to do.
2) You can make something static so that you can access the same instance from within mHello.
3) But I think the best answer is to use the structure that's commonly used: Make the UI a class. It has member variables to which the member functions have access. If mColor and mHello are members of the same class, then they can access whatever they need, pretty easily. Then you create some means of getting stuff outside the UI class so that it can be accessed, processed, and returned by whatever application you're writing.
(old answer:)
There are two ways.
EDIT: Both of which have already been suggested.
You could either make both of those functions members of the same class, and then x (which you should name something more descriptive) is accessible to both of them without being global.
Or...
You could (as suggested by other commentors) pass x as a parameter to your second function. mColor() even returns x which means you can store that value in another variable that you then pass to mhello().
Incidentally, I'm not sure why you're prefacing your methods with m but that typically identifies them as members of a class. Are they?
It would also be helpful to see the rest of the code, where you're actually calling these functions.

Related

Choosing from a List of methods in a tkinter Button

Good Day,
I'm new to this forum (and quite new to programming), so I hope my question is properly formulated.
I've been trying to create a GUI in python using tkinter, and I want to have two buttons calling methods of two different classes. One method is defining an integer, the second one is reporting content. I'd have a list of objects of the latter class, and I want to choose the right instance by the integer. Here's a MWE:
import tkinter as tk
class data:
def __init__(self, content):
self.content = content
def report(self):
print("This is reported as self.content:" + str(self.content)) #This doesnt report the correct value for some reason?
print("The Class does register the correct idx:" + str(Selector.idx))
print("Using the Dict the correct value can be returned:" + str(vocables[Selector.idx].content))
class increment:
def __init__(self):
self.idx = 0
def increase(self):
self.idx += 1
print(self.idx)
vocables[self.idx].report()
root = tk.Tk()
Selector = increment()
vocables = []
for id in range(10):
vocables.append(data(id))
# print(vocables[id].content)
CheckVocable = tk.Button(root, text="Report", command=vocables[Selector.idx].report)
CheckVocable.pack()
NextVocable = tk.Button(root, text="Increase Index", command=Selector.increase)
NextVocable.pack()
root.mainloop()
I do not understand why the print of line 8 always reports the value of the first item in the list (vocabules[0] in this instance) instead of my desired value, which is returned in all other print cases. Am I messing up the work with classes or is the button behavior confusing me?
Thanks in advance!

Returning PY_VARxxx instead of expected string

I'm currently creating a GUI in order to turn a lot of individual instruments into one complete system. In def smuSelect(self) I create a list self.smuChoices I can use to call individual choices such as smuChoices[0] and it will return "2410(1)".
Once I call def checkBoxSetup it returns PY_VARxxx. I've tried searching the different forums and everything. I've seen mentions using the .get() which just gives me the state of the individual choice. The reason I want the actual string itself is I would like to use it in def testSetup(self) for the user to assign specific names to the individual machine, for example, 2410 = Gate.
My initial attempt was to create another variable smuChoice2 but I believe this is still changing the original list self.smuChoices.
import tkinter as tk
import numpy as np
from tkinter import ttk
def checkBoxSetup(smuChoice2): #TK.INTVAR() IS CHANGING NAME OF SMUS NEED TO CREATE ANOTHER INSTANCE OF SELF.SMUCHOICES
for val, SMU in enumerate(smuChoice2):
smuChoice2[val] = tk.IntVar()
b = tk.Checkbutton(smuSelection,text=SMU,variable=smuChoice2[val])
b.grid()
root = tk.Tk()
root.title("SMU Selection")
"""
Selects the specific SMUs that are going to be used, only allow amount up to chosen terminals.
--> If only allow 590 if CV is picked, also only allow use of low voltage SMU (maybe dim options that aren't available)
--> Clear Checkboxes once complete
--> change checkbox selection method
"""
smuChoices = [
"2410(1)",
"2410(2)",
"6430",
"590 (CV)",
"2400",
"2420"
]
smuChoice2 = smuChoices
smuSelection = ttk.Frame(root)
selectInstruct = tk.Label(smuSelection,text="Choose SMUs").grid()
print(smuChoices[0]) #Accessing list prior to checkboxsetup resulting in 2410(1)
checkBoxSetup(smuChoice2)
print(smuChoices[0]) #Accessing list after check box setup resulting in PY_VAR376
variableSMUs = tk.StringVar()
w7_Button = tk.Button(smuSelection,text="Enter").grid()
w8_Button = tk.Button(smuSelection,text="Setup Window").grid()
root.mainloop()
I was able to solve the problem by changing my list, smuChoices, to a dictionary then modifying
def checkBoxSetup(smuChoice2):
for val, SMU in enumerate(smuChoice2):
smuChoice2[val] = tk.IntVar()
b = tk.Checkbutton(smuSelection,text=SMU,variable=smuChoice2[val])
b.grid()
to
def checkBoxSetup(self):
for i in self.smuChoices:
self.smuChoices[i] = tk.IntVar()
b = tk.Checkbutton(self.smuSelection,text=i,variable=self.smuChoices[i])
b.grid()
Previously I was replacing the variable with what I'm guessing is some identifier that tkinter uses to store the state which is why I was getting PYxxx.
First of all getting PY_VARXX instead of what's in a variable class indicates the lack of get().
replace:
print(self.smuChoices[0])
with:
print(self.smuChoices[0].get())
Secondly, if you want to display the value of a variable class on a label, button, etc. you could rather just use the textvariable option by simply assigning the variable class to it.
Replace:
tk.Label(self.smuName,text=SMU).grid()
with:
tk.Label(self.smuName, textvariable=self.smuChoices[val]).grid()
Your question is still a bit unclear to me but I will try to provide an answer to the best of my understanding.
As I understand it, you're trying to create a set of Checkbuttons for a given list of items. Below is an example of a method that takes items as an argument and returns a dictionary of checkboxes that have root as their parent:
import tkinter as tk
def dict_of_cbs(iterable, parent):
if iterable:
dict_of_cbs = dict()
for item in iterable:
dict_of_cbs[item] = tk.Checkbutton(parent)
dict_of_cbs[item]['text'] = item
dict_of_cbs[item].pack() # it's probably a better idea to manage
# geometry in the same place wherever
# the parent is customizing its
# children's layout
return dict_of_cbs
if __name__ == '__main__':
root = tk.Tk()
items = ("These", "are", "some", "items.")
my_checkboxes = dict_of_cbs(items, root)
root.mainloop()
Additionally note that I haven't used any variable classes (BooleanVar, DoubleVar, IntVar or StringVar) as they seem to be redundant in this particular case.

Problems with a bind function from tkinter in Python

I am working on an application that is supposed to support both running from a console and from a GUI. The application has several options to choose from, and since in both running modes the program is going to have the same options obviously, I made a generalisation:
class Option:
def __init__(self, par_name, par_desc):
self.name = par_name
self.desc = par_desc
class Mode():
def __init__(self):
self.options = []
self.options.append(Option('Option1', 'Desc1'))
self.options.append(Option('Option2', 'Desc2'))
self.options.append(Option('Option3', 'Desc3'))
self.options.append(Option('Option4', 'Desc4'))
self.options.append(Option('Option5', 'Desc5'))
#And so on
The problem is that in GUI, those options are going to be buttons, so I have to add a new field to an Option class and I'm doing it like this:
def onMouseEnter(par_event, par_option):
helpLabel.configure(text = par_option.desc)
return
def onMouseLeave(par_event):
helpLabel.configure(text = '')
return
class GUIMode(Mode):
#...
for iOption in self.options:
iOption.button = Button(wrapper, text = iOption.name, bg = '#004A7F', fg = 'white')
iOption.button.bind('<Enter>', lambda par_event: onMouseEnter(par_event, iOption))
iOption.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))
#...
There is also a "help label" showing the description of the option every time a mouse hovers over it, so there I am binding those functions.
What is happening is that while I am indeed successfully adding a new field with a button, the bind function seems to mess up and the result is this:
Help label is always showing the description of the last option added, no matter over which button I hover. The problem seems to go away if I directly modify the Option class instead, like this:
class Option:
def __init__(self, par_name, par_desc):
self.name = par_name
self.desc = par_desc
self.button = Button(wrapper, text = self.name, bg = '#004A7F', fg = 'white')
self.button.bind('<Enter>', lambda par_event: onMouseEnter(par_event, self))
self.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))
But I obviously can't keep it that way because the console mode will get those fields too which I don't really want. Isn't this the same thing, however? Why does it matter if I do it in a constructor with self or in a loop later? I therefore assume that the problem might be in a way I dynamically add the field to the class?
Here is the full minimal and runnable test code or whatever it is called, if you want to mess with it: http://pastebin.com/0PWnF2P0
Thank you for your time
The problem is that the value of iOption is evaluated after the
for iOption in self.option:
loops are complete. Since you reset iOption on each iteration, when the loop is completed iOption has the same value, namely the last element in self.options. You can demonstrate this at-event-time binding with the snippet:
def debug_late_bind(event):
print(iOption)
onMouseEnter(event, iOption)
for iOption in self.options:
iOption.button = Button(wrapper, text = iOption.name,
bg = '#004A7F', fg = 'white')
iOption.button.bind('<Enter>', debug_late_bind)
which will show that all events that iOption has the same value.
I split out the use of iOption to debug_late_bind to show that iOption comes in from the class scope and is not evaluated when the bind() call is executed. A more simple example would be
def print_i():
print(i)
for i in range(5):
pass
print_i()
which prints "4" because that is the last value that was assigned to i. This is why every call in your code to onMouseEnter(par_event, iOption) has the same value for iOption; it is evaluated at the time of the event, not the time of the bind. I suggest that you read up on model view controller and understand how you've tangled the view and the controller. The primary reason this has happened is that you've got two views (console and tk) which should be less coupled with the model.
Extracting the .widget property of the event is a decent workaround, but better still would be to not overwrite the scalar iOption, but instead use list of individual buttons. The code
for n, iOption in enumerate(self.options):
would help in creating a list. In your proposed workaround, you are encoding too much of the iOption model in the tkinter view. That's bound to bite you again at some point.
I don't know what the actual problem was with my original code, but I kind of just bypassed it. I added a dictionary with button as a key and option as a value and I just used the par_event.widget to get the option and it's description, which is working fine:
buttonOption = {}
def onMouseEnter(par_event):
helpLabel.configure(text = buttonOption[par_event.widget].desc)
return
def onMouseLeave(par_event):
helpLabel.configure(text = '')
return
class GUIMode(Mode):
def run(self):
#...
for iOption in self.options:
iOption.button = Button(wrapper, text = iOption.name, bg = '#004A7F', fg = 'white')
iOption.button.bind('<Enter>', lambda par_event: onMouseEnter(par_event))
iOption.button.bind('<Leave>', lambda par_event: onMouseLeave(par_event))
buttonOption[iOption.button] = iOption
#...

Executing different functions based on options selected

I wanted to know if there is a way of populating a option menu with choices and then each of the different choice gives the build button a different function
for eg:
Type = cmds.optionMenu('type',w = 300 ,label = 'Type of crowd:')
cmds.menuItem( label='Walking' )
cmds.menuItem( label='Running' )
cmds.menuItem( label='Cheering' )
cmds.button('Build',command = bld)
def walk(*args):
print (walking)
def run(*args)
print (running)
def cheer(*args)
print (cheer)
so if the menu item selected would be walking the button command would execute the command wak
and if the menu item selected would be running then the button command would execute the command run and so on....
is this even possible in maya python...????
There's three parts to the problem.
First, you want your options to be callables. I like functools.partial for that, so that you could give the same command different parameters and have it be treated as two different actions:
from functools import partial
bigcircle = functools.partial ( cmds.circle, radius = 10)
littleCircle = functools.partial (cmds.circle, radius = 1)
the second problem is that menuItems in OptionMenus don't fire their commands directly. They trigger the -cc change command on the owning optionMenu. So we need something that will turn the label back into a callable object. A little class will do:
class menuMgr(object):
'''call the function associated with a key in the **callables dictionary'''
def __init__(self, **callables):
self.Callables = callables
def __call__(self, *args):
self.Callables[args[-1]]()
The third part is to match these with a label. You can do this elegantly with the **kwargs syntax, where you can either pass in a whole dictionary or named keywords:
def menu_of_functions(**callables):
mmgr = menuMgr(**callables)
Main = cmds.optionMenu('type3',w = 300 ,label = 'Type of crowd:', cc = mmgr)
for key, partial in callables.items():
cmds.menuItem(label = key)
cmds.setParent("..")
Heres the whole thing in working form to inspect:
import maya.cmds as cmds
import functools
bigCircle = functools.partial ( cmds.circle, radius = 10)
littleCircle = functools.partial (cmds.circle, radius = 1)
class menuMgr(object):
def __init__(self, **callables):
self.Callables = callables
def __call__(self, *args):
self.Callables[args[-1]]()
def menu_of_functions(**callables):
mmgr = menuMgr(**callables)
Main = cmds.optionMenu('type3',w = 300 ,label = 'Type of crowd:', cc = mmgr)
for key, partial in callables.items():
cmds.menuItem(label = key)
cmds.setParent("..")
q = cmds.window()
cmds.columnLayout()
menu_of_functions(big = bigCircle, small = littleCircle)
cmds.showWindow(q)
Sure you can, since functions are first class objects in python.
Let's say you have a list of functions.
fns = [walk, run, cheer]
1) You'll need a mapping from a string key to the python function.
Let's use a dictionary comprehension.
options = dict((fn.__name__, fn) for fn in fns)
Alternatively you can build the dictionary with arbitrary keys.
options = {"Walking": walk, "Running": run, "Cheering": cheer}
2) Get a reference to the function by accessing the dictionary item with the functions name.
options['run'](*args)
3) ???
4) Profit
Had a chance to do a small bit of research on this today, but I don't actually use Maya Python so this may not be viable code -- at least it should be close!
def walk(*args):
print ("walking")
def run(*args)
print ("running")
def cheer(*args)
print ("cheer")
fncdict = {"Walking":walk,"Running":run,"Cheering":cheer}
def dofunc(funcname):
try: fncdict[funcname]()
except KeyError: print("adsmith doesn't really know how this crap works,
and gave you some really shoddy advice. Go downvote
his answer.")
Type = cmds.optionMenu('type',w = 300 ,label = 'Type of crowd:')
# Please don't name things this way! naming conventions in PEP 8
# make this look like a class not an instance of optionMenu
cmds.menuItem( label='Walking', parent = Type )
cmds.menuItem( label='Running', parent = Type )
cmds.menuItem( label='Cheering', parent = Type )
cmds.button('Build',command = lambda x: dofunc(Type.value))
# I'm assuming this is the button you want to use to say "GO", right?
From the little I've read, it looks like optionMenu.value refers to the text in the active menuItem, but I can't say for sure -- it may just be the text for that optionMenu in which case the button will call dofunc('Type of crowd:') which will return the exception I built to shame myself.
Here's an alternative that I KNOW will work, but it's ugly and unnecessary.
# all
# those
# functions
# go
# here
# including
# the dict and dofunc
activeMenuItem = None
Type = cmds.optionMenu('type',w = 300 ,label = 'Type of crowd:',
changeCommand = lambda x: activeMenuItem = x)
# this fails because you can't do assignments in a lambda -- OOPS!!
cmds.menuItem( label='Walking', parent = Type )
cmds.menuItem( label='Running', parent = Type )
cmds.menuItem( label='Cheering', parent = Type )
cmds.button('Build',command = lambda x: dofunc(activeMenuItem))
The changeCommand option in optionMenu gets called every time you change items. I've assigned it to a lambda that updates the variable activeMenuItem to the value in the newly-active menuItem, then had the button reference the variable instead of querying the optionMenu for its currently selected button, but let's be honest -- this is what menus are MADE to do. There is DEFINITELY a way to do it without storing every single selection.
EDIT: THIS LAST ONE WON'T WORK BECAUSE YOU CAN'T DO ASSIGNMENTS WITHIN LAMBDA EXPRESSIONS. MY FAULT!
I have kind of figured out an easier way to this problem
cmds.optionMenu('Greetings',Label = 'Greet')
cmds.menuItem(label= hi,parent = 'Greetings)
cmds.menuItem(label = Hello,parent = 'Greetings')
def Greet(*args)
menuItems = cmds.optionMenu('Greetings',q=True,v=True)
if menuItems = hi
print "hi"
menuItemsnew = cmds.optionMenu('Greetings',q=True,v=True)
if menuItemsnew = hello
print "hello"
This should work,it worked for me

Tkinter, saving functions to a list and then running them

I'm working on a GUI for a project in school. All the buttons that I have in my GUI are bound with functions that I have created. These functions call for already predefined functions. For some of the predefined functions, I need one or two arguments and I have solved that with entries. I type in the arguments in the right entries that are connected to the specific button and when I press the button, the function will run with the corresponding arguments.
The thing I want to do is to in some way when I press a button, the function should be saved to a list instead of being executed right away. And when I push the "run" button(a new button that I will create) everything in my list will be executed. I have been thinking about using a list box but I don't know exactly how they work or if its even possible to run a list box that contains a number of functions. Does someone have any ideas or solutions for me? Can I use the list box for this or is there something else that is better to use?
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.entry1 = IntVar()
self.entry2 = IntVar()
def do_something():
value1 = self.entry1.get()
value2 = self.entry2.get()
self.listbox.insert(END, "predefined_function(value1, value2)")
def run_listbox_contents():
pass
self.button = Button(frame, text="Move", command=lambda: do_something())
self.button.pack(side=TOP)
self.entry1.set("value1")
self.entry = Entry(frame, textvariable=self.entry1)
self.entry.pack(side=TOP)
self.entry2.set("value2")
self.entry = Entry(frame, textvariable=self.entry2)
self.entry.pack(side=TOP)
self.listbox = Listbox(master)
self.listbox.pack(side=TOP)
root = Tk()
app = App(root)
root.title("Mindstorms GUI")
root.geometry("800x1200")
root.mainloop()
root.destroy()
Just use a standard list.
something like this
def hest(txt):
print "hest: " +txt
def horse(txt):
print "horse: " + txt
funcList = []
funcList.append(hest)
funcList.append(horse)
for x in funcList:
x("Wow")
This outputs
hest: Wow
horse: Wow
Was this what you wanted?
If I were you, I wouldn't want to save functions to a list. I would suggest another solution for you.
I suppose you have heard of the principle of MVC (Model-View-Controller). In your case, the list box is a part of view, and the process that saves functions and then calls them at once is a part of controller. Separate them.
You might want to save and display any string in the list box to let the users know that the corresponding functions have been enlisted and ready to run. For example, save a string "Function1 aug1 aug2 aug3" or "Funtion2 aug1 aug2" or whatever you like as a handle of the corresponding function.
And for the controller part, write a function (let's say conductor()). It reads the handle strings from the list, parses them and calls the corresponding functions. Where you want to run the enlisted functions, there you just call conductor().
Update:
Due to your comment I understand that you are pretty new to program. Let me show you how to write a simplest parser with your given variable names.
def run_listbox():
to_do_list = #get the list of strings
for handle_string in to_do_list:
#Let's say you got
#handle_string = "Predfined_function1 value1 value2"
#by here
handle = handle_string.split(" ")
#Split the string by space, so you got
#handle = ["Predfined_function1", "value1", "value2"]
#by here
if handle[0] == "Predfined_function1":
Predfined_function1(handle[1], handle[2]) #Call Predfined_function1(value1, value2)
elif handle[0] == "Predfined_function2":
Predfined_function2(handle[1], handle[2])
#elif ...
#...
#elif ...
#...
#elif ...
#...
This is not a perfect parser, but I hope it could let you know what does a parser look like.

Categories