Trying to create a dynamic menu with a common command function - python

I'm trying to dynamically create a RECENT FILES menu from a list, then use a common function to process the selected file.
Windows 7, Python 2.7
I can successfully create the menu, but haven't been able to find a way to pass an indicator to the common function to identify the user's selection. I keep slamming up against Python's pass-by-reference; no matter what I try, the last menu argument value created is passed to the command function.
I've searched extensively, and nothing I've found has helped. (I ALMOST got it working with an 'exec', but I'm doing this processing inside a class and 'exec' doesn't seem to like self. functions)
I don't care what I can get - an index, a file name, ANYTHING I can use to determine which entry has been selected. If I'm completely off track with my approach, PLEASE point me elsewhere. I'm a Python newbie, but a veteran programmer and would truly appreciate constructive guidance/ criticism.
Here's my last attempt, which simply demonstrates my problem. Everything I've tried has the same result - only ever returning the last list iteration:
from Tkinter import *
def processFile(fileIndex):
print fileIndex
file_list = [('01','File01'),('02','File02'),('03','File03')]
root = Tk()
menu_bar = Menu(root)
file_menu = Menu(menu_bar)
recent_menu = Menu(file_menu)
Menu(file_menu)
for i, file in enumerate(file_list):
file_display = '%d %s' % (int(file[0]), file[1]) # Just making the menu pretty
recent_menu.add_command(label=file_display, command=lambda: processFile('%d' % i))
file_menu.add_cascade(label='Recent Files', menu=recent_menu)
menu_bar.add_cascade(label="File", menu=file_menu)
root.config(menu=menu_bar)
mainloop()

recent_menu.add_command(label=file_display, command=lambda i=i: processFile(i))
If you do not use the i=i, the lambda variable are evaluated at run time, in stead of declaration time. - This is default for lambda functions.

Related

how to make selected object renamed in maya using python?

I have a python function here that is supposed to rename object in maya. But when the window showed up and i click the 'rename' button, nothing changed. not even bringing new window. please help
def renameObject():
a = cmds.ls(sl=True)
txt = cmds.textField('txtName', q=True, tx=True)
cmds.rename('a', txt)
cmds.confirmDialog(icn='information', message='Done!')
cmds.showWindow()
return
cmds.window(title='Rename Object')
cmds.columnLayout(adj=1)
cmds.text(label= 'Insert Name', w=300, h=30)
cmds.separator()
cmds.textField('txtName')
cmds.button(label='Rename', width=300, c=lambda*args:'renameObject()')
cmds.showWindow()
Two reasons:
Your lambda expression has a string 'renameObject()', it should be the function name without apostrope.
Even if the renameObject() function is called, it will fail because you assign the current selection to a variable called a. But in the rename function, you use again a string 'a'. So maya searches for an object called 'a' and tries to rename it what does not work unless you really have an object called 'a'.
And the confirmDialog() does not need a cmds.showWindow(), it works without.
def renameObject(*args):
a = cmds.ls(sl=True)
txt = cmds.textField(txt_field , q=True, tx=True)
cmds.rename(a[0], txt)
cmds.confirmDialog(icn='information', message='Done!')
cmds.showWindow()
cmds.window(title='Rename Object')
cmds.columnLayout(adj=1)
cmds.text(label= 'Insert Name', w=300, h=30)
cmds.separator()
txt_field = cmds.textField('txtName')
cmds.button(label='Rename', width=300, c=renameObject)
cmds.showWindow()
I've corrected your code, it shuold work when ran but there is lots of mistakes in your code, Haggi Krey has pointed lots of them. If you want to dive in UI design, you should look at partial module from functools. There is lots of examples here in Stack

Elegant solution about class and referencing

I've recently started using classes in my programs and I've got 2 problems/questions.
Following the question I've pasted not the whole code, but only the part about which I need advise about.
1)the only way I have found to access to the value the user will input in my GUI's entry widget is to declare the list ac global, so I can later append the various entry widget for further reference. I tried it in my printer function and it worked. I was just wondering if there is another way to manage the problem because using a global variable, according to what I read on the net, is not the very best idea.
2)I've got some problem with doing the same thing with the optionwidget. I can't find a way to get the data because of variable referencing problems. Assuming that I'm managing this thing the best way an apprentice of Python could, how can I do that?
If I wasn't clear, please let me know. I'm not a native English speaker and I still have to get confidence with the language.
from tkinter import *
root = Tk ()
root.title("GENERATOR")
root.geometry("300x300")
class entryandlabel:
global ac
ac=[]
def printer():
#as an example
print(ac[2].get())
def createentry(lista):
#entry widget creation
i=1
for a in lista:
entry = Entry (root)
entry.grid(row=i, column=1, sticky = W)
ac.append(entry)
i+=1
def createoption(lista, c):
#option widget creation
a = StringVar(root)
a.set("Selezionare")
b = OptionMenu(*(root, a) + tuple(lista))
b.grid(row=c, column=1)

Python: Closing a sub-child-window prevents the opening of a new sub-child

For my job in a laboratory of my University of Applied Sciences I need to create a Python-programm which creates a child-windows with the possibility to create another one.
So far this works quite fine.
The tricky thing is where I close the childrens child and try to open a new "grandchild" of the main-window.
Closing and opening also works fine on the level of the first child. I can enter that child, go back to the main menu and so on as long I wish.
Here the code I am working on right now:
import tkinter
def Praktika():
global Praktika
Praktika = tkinter.Toplevel(main)
Praktika.geometry("320x200")
Prak1 = tkinter.Button(Praktika, text="Praktikum 1", command =Praktikum1)
Prak1.pack()
Haupt = tkinter.Button(Praktika, text="Hauptmenu", command = ClosePraktika)
Haupt.pack()
def ClosePraktika():
Praktika.destroy()
def Praktikum1():
global Praktikum1
Praktikum1 = tkinter.Toplevel(main)
Praktikum1.geometry("320x200")
Haupt = tkinter.Button(Praktikum1, text="Hauptmenu", command = ClosePraktikum1)
Haupt.pack()
def ClosePraktikum1():
Praktika.destroy()
Praktikum1.destroy()
def CloseAll():
main.quit()
main = tkinter.Tk()
main.geometry("320x200")
main.title("Fueh")
tkinter.Button(main, text="Praktika", command=Praktika).pack()
tkinter.Button(main, text="Exit", command=CloseAll).pack()
main.mainloop()
This is now the third attempt until now and ffter the research I have done I start to think that handling sub-children ain't that easy as I think.
So well,
already thank you very much for the help!
The problem is that you have a function named Praktikum1, and then you create a global variable named Praktikum1 which causes the function to be destroyed. So, the next time you call the function, you're actually "calling" the variable.
Don't use the same name for global variables and for functions.

Creating A UI Window For My Previous Script

folks! So, thanks to you guys I was able to figure out what it was I was doing wrong in my previous script of staggering animation for selected objects in a scene. I am now on part two of this little exercise: Creating a UI for it.
This involves creating a window with a button and user input of how much the animation will be staggered by. So, instead of me putting how much the stagger should increment by (which was two in my previous script), I'd now allow the user to decide.
The script I have so far created the window, button, and input correctly, though I am having some trouble with getting the UI to properly execute, meaning when I click on the button, no error pops up; in fact, nothing happens at all to change the scene. I get the feeling it's due to my not having my increment variable in the correct spot, or not utilizing it the right way, but I'm not sure where/how exactly to address it. Any help would be greatly appreciated.
The code I have (with suggested edits) is as follows:
import maya.cmds as cmds
spheres = cmds.ls(selection=True)
stagWin = cmds.window(title="Stagger Tool", wh=(300,100))
cmds.columnLayout()
button = cmds.button(label="My Life For Aiur!")
count = cmds.floatFieldGrp(fieldgroup, query=True, value=True)
fieldgroup = cmds.floatFieldGrp(numberOfFields=1)
cmds.button(button, edit=True, command=lambda _:stagger(fieldgroup))
cmds.showWindow(stagWin)
def stagger(fieldgroup):
for i in spheres:
cmds.selectKey(i)
cmds.keyframe(edit=True, relative=True, timeChange=count)
print "BLAH"
Moving the comments into an answer because I think I've got it all figured out finally:
First of all, the better practice is to pass the stagger object to the button command rather than the string. so that would be:
cmds.button(label="My Life For Aiur!", command=stagger)
Secondly, the count isn't getting updated, so it stays 0 as per your third line. To update that:
count = cmds.floatFieldGrp(fieldgroup, query=True, value=True)
But wait, where did fieldgroup come from? We need to pass it into the function. So go back to your button code and take out the command entirely, also saving the object to a variable:
button = cmds.button(label="My Life For Aiur!")
Now store the object for the fieldgroup when you make it:
fieldgroup = cmds.floatFieldGrp(numberOfFields=1)
Now that you have fieldgroup, you can pass that in the function for the button, like this:
cmds.button(button, edit=True, command=lambda _:stagger(fieldgroup))
I had to wrap the function in a lambda because we're passing fieldgroup, but if I just put stagger(fieldgroup) it would call that and pass the result of that into the command for the button
Also update stagger def with fieldgroup argument:
def stagger(fieldgroup):
One final note that won't actually affect this, but good to know:
when you shift the keyframes inside stagger you're using a DIFFERENT count variable than the one you declared as 0 up above. The outer one is global, and the inner is local scope. Generally it's best to avoid global in the first place, which fortunately for you means just taking out count = 0
Putting that all together:
import maya.cmds as cmds
spheres = cmds.ls(selection=True)
stagWin = cmds.window(title="Stagger Tool", wh=(300,100))
cmds.columnLayout()
button = cmds.button(label="My Life For Aiur!")
fieldgroup = cmds.floatFieldGrp(numberOfFields=1)
cmds.button(button, edit=True, command=lambda _:stagger(fieldgroup))
cmds.showWindow(stagWin)
def stagger(fieldgroup):
count = 0
increment = cmds.floatFieldGrp(fieldgroup, query=True, value=True)[0]
print count
for i in spheres:
cmds.selectKey(i)
cmds.keyframe(edit=True, relative=True, timeChange=count)
count += increment
print "BLAH"

boolean not working correctly

I'm trying to get the following code to create 2 buttons which when you press one button will show the fader and when the other button is pressed will hide the fader, but obviously this isn't working i think this is mainly because i can't get my head around how booleans work in python so if somebody could help me i would greatly appreciate it.
from tkinter import *
#first window
master= Tk()
master.geometry('1440x900+0+0')
master.title('DMX512 Controller')
#buttons
bw=250
bh=110
bool1show = False
Button(master,text="show the slider", command =bool1show= True).place(x=800,y=10)
Button(master,text="hide the slider", command = bool1show= not True).place(x=900,y=10)
#slider characteristics
slw=130
sll=600
sly=1
stc='blue'
if bool1show==True:
Scale(master, from_=255, to=0, length =sll,width =slw, troughcolor = stc).grid(row=sly,column=5)
if bool1show==not True:
Scale(from_=255, to=0, length =sll,width =slw, troughcolor = stc).grid(row=sly,column=5)
You have to give a reference to a function in the command parameter, and bool1show= True is not a reference to a function. However, since all you're doing is showing or hiding a widget, you don't need to use a boolean variable at all unless you're using radiobuttons (which you're not).
For the code you've posted, you just need two functions: one to show the slider and one to hide it. To do that, you create the Scale once, and then use the grid methods to show and hide it. To make that work, you have to "remember" the scale widget by saving a reference in a global variable.
def show_scale():
# note: grid will remember the values you used when you first called grid
the_scale.grid()
def hide_scale():
# this removes the scale from view, but remembers where it was
the_scale.grid_remove()
the_scale = Scale(master, from_=255, to=0, length =sll,width =slw, troughcolor = stc)
the_scale.grid(row=sly, column=5)
...
Button(master,text="show the slider", command show_scale).place(x=800,y=10)
Button(master,text="hide the slider", command hide_scale).place(x=900,y=10)
On an unrelated note, I strongly encourage you to not use place. Your GUIs will be easier to write and maintain, and they will behave better when being resized or run on systems with different resolutions or different fonts.
How about if you assign the command argument to a lambda function that calls another function:
Button(master,text="show the slider", command=lambda: bool_func(bool1show).place(x=800,y=10)
Button(master,text="hide the slider",command=lambda: bool_func(bool1show,b=False).place(x=900,y=10)
where:
def bool_func(variable, b=True): # b is True by default, hence you don't provide the argument in the first Button statement.
variable = b
return variable
for some info on lambda functions, look here
There is some ambiguity in your code so ii'm not sure what you are trying to achieve. If you want to verify a True condition you can do:
if bool1show == True:
do stuff...
#or
if bool1show:
do stuff...
if bool1show==not True: does not work. If you really want to go like this you can do:
if bool1show==(not True)
#or better:
if bool1show == False
#even better:
if not bool1show:
Hope this helps understand better python booleans

Categories

Resources