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"
Related
First of all this is my code:
button_1 = Button(
image=button_image_1,
borderwidth=0,
highlightthickness=0,
command=lambda:[get_to_main_when_clicked(),delete_red_border()],
relief="flat"
)
As you can see I binded 2 functions to this button. To the first one: If a specific condition is true, then the function lets an image appear. The second one then should wait 3 seconds and should delete the appeared image. The only really weird problem is, that it no matter what I do, first executes the delete_red_border() function. It waits 3 seconds, then its trying to delete an image that couldn't be defined and globalized, because the get_to_main_when_clicked() function wasn't executed. How can I solve this?
PS: The specific condition is true.
Don't do this. Create a function specifically for this button. A function is much easier to understand and debug than a lambda, especially a complex lambda. You can then put any logic you want inside the function.
def do_something():
get_to_main_when_clicked()
delete_red_border()
button_1 = Button(..., command=do_something)
I found the solution for it. The problem was, that it didn't refreshed/updated the window after finishing the first function. That was solved by one line of code:
window.update()
New to GUI. Not quite getting there. I used page and get can get buttons to do something (click on a button and get a response). With Combobox, I can't pass a value. Searched here, tried many things, watched a few hours of youtube tutorials.
What am I doing wrong below? This is the code page generates (basically) then I added what I think I need to do to use the Combobox.
I am just trying to have 1,2,3 in a combo box and print out the value that is chosen. Once I figure that out I think I can actually make a simple GUI that passes variables I can then program what I want to do with these variables being selected.
class New_Toplevel_1:
def __init__(self, top):
self.box_value = StringVar()
self.TCombobox1 = ttk.Combobox(textvariable=self.box_value)
self.TCombobox1.place(relx=0.52, rely=0.38, relheight=0.05, relwidth=0.24)
self.TCombobox1['values']=['1','2','3']
self.TCombobox1.configure(background="#ffff80")
self.TCombobox1.configure(takefocus="")
self.TCombobox1.bind('<<ComboboxSelected>>',func=select_combobox)
def select_combobox(self,top=None):
print 'test combo ' # this prints so the bind works
self.value_of_combo = self.ttk.Combobox.get() # this plus many other attempts does not work
It's hard to know what you're actually asking about, since there is more than one thing wrong with your code. Since you say the print statement is working, I'm assuming the only problem you have with your actual code is with the last line.
To get the value of the combobox, get the value of the associated variable:
self.value_of_combo = self.box_value.get()
Here's a working version where I fixed the other things that were wrong with the program:
from tkinter import *
from tkinter import ttk
class New_Toplevel_1:
def __init__(self, top):
self.box_value = StringVar()
self.TCombobox1 = ttk.Combobox(textvariable=self.box_value)
self.TCombobox1.place(relx=0.52, rely=0.38, relheight=0.05, relwidth=0.24)
self.TCombobox1['values']=['1','2','3']
self.TCombobox1.configure(background="#ffff80")
self.TCombobox1.configure(takefocus="")
self.TCombobox1.bind('<<ComboboxSelected>>',func=self.select_combobox)
def select_combobox(self,top=None):
print('test combo ') # this prints so the bind works
self.value_of_combo = self.box_value.get()
print(self.value_of_combo)
root = Tk()
top = New_Toplevel_1(root)
root.mainloop()
Note: I strongly advise you not to start with place. You should try to learn pack and place first. I know place seems easier, but to get the most responsive and flexible GUI you should leverage the power of pack and grid.
Here, is the piece of code that I have written. I have to make Open and Save Button functional. So, my both functions are working fine. I am able to Save and Load UI but the basic problem is that after loading, when I click on add rows, the rows doesn't added in below the already existed row. It has been a week working on it. I am in trouble and doesnt know the wayout
from tkinter import *
import dill
from collections import OrderedDict
class Program:
def __init__(self):
self.row=0
self.entries=[]
self.current_widget=0
self.Ordered_dictionary_for_entry_widget=OrderedDict()
self.values_for_entry_dictionary=[]
self.AddButton = Button(text="Add Row", command=self.add_button_command)
self.AddButton.grid(column=0,row=0)
self.save_button=Button(text="save",command=self.save_button_command)
self.save_button.grid(column=0,row=1)
self.load_button=Button(text="Open",command=self.Open_button_command)
self.load_button.grid(column=0,row=2)
self.total_entries_length=len(self.entries)
def add_button_command(self):
self.entry=Entry()
self.entry.grid(column=1,row=self.row)
self.entries.append(self.entry)
self.row=self.row+1
def save_button_command(self):
self.total_entries_length=len(self.entries)
print(self.total_entries_length)
for widget in self.entries:
self.Ordered_dictionary_for_entry_widget["Name"+str(self.current_widget+1)]=widget.get()
self.current_widget=self.current_widget+1
with open("example_fully_functional.txt","wb") as f:
dill.dump(self.Ordered_dictionary_for_entry_widget,f)
def Open_button_command(self):
print("Total entries length",self.total_entries_length)
with open("example_fully_functional.txt","rb") as f:
self.Ordered_dictionary_for_entry_widget=dill.load(f)
for key,values in self.Ordered_dictionary_for_entry_widget.items():
self.values_for_entry_dictionary.append((values))
print(self.values_for_entry_dictionary)
for i in (self.values_for_entry_dictionary):
self.entry=Entry()
self.entry.grid(column=1,row=i)
self.entries.append(self.entry)
print("Entry loaded",self.entries_loaded)
#Insert the entries back into the UI
[self.entries.insert(0,self.values_for_entry_dictionary) for
self.entries,self.values_for_entry_dictionary in
zip(self.entries,self.values_for_entry_dictionary)]
program = Program()
mainloop()
Ok to answer the direct question: self.row is not incremented in Open_button_command so it is inaccurate when add_button_command tries to add a new Entry,
for i in (self.values_for_entry_dictionary):
self.entry=Entry()
self.entry.grid(column=1,row=i)
self.entries.append(self.entry)
## THIS ONE HERE ##
self.row+=1
#####
I want to suggest a better solution then keeping track of the next column in a variable but before I can we need to fix up a few things, first in Open_button_command:
for i in (self.values_for_entry_dictionary):
self.entry=Entry()
self.entry.grid(column=1,row=i)
...
You are iterating over the values that need to be inserted into the entries not the indices, to get the indices to use in .grid you can use range(len(X)) instead:
for i in range(len(self.values_for_entry_dictionary)):
or better yet use enumerate to make the Entries and fill them at the same time:
for i,value in enumerate(self.values_for_entry_dictionary):
self.entry=Entry()
self.entry.grid(column=1,row=i)
self.entry.insert(0,value)
...
this way you don't need this:
[self.entries.insert(0,self.values_for_entry_dictionary) for
self.entries,self.values_for_entry_dictionary in
zip(self.entries,self.values_for_entry_dictionary)]
which overrides self.entries and self. self.values_for_entry_dictionary during the loop so a lot of information gets messed up during that, use enumerate instead.
Once that is cleaned up and self.entries will consistently be a list of all the Entry widgets in window the self.row should always be equal to len(self.entries) so it would be much preferable to use a property to calculate it every time:
class Program:
#property
def row(self):
return len(self.entries)
...
Then comment out any statement trying to set self.row=X since you don't have or need a setter for it. Every time you use self.row it will calculate it with the property and add_button_command will always add a new entry to the bottom.
However in Open_button_command you are still creating new widgets even if there is already an Entry in the window, it would make more sense to check if there is already one that can be reused:
def Open_button_command(self):
...
for i,value in enumerate(self.values_for_entry_dictionary):
if i<len(self.entries):
self.entry = self.entries[i]
self.entry.delete(0,"end") #remove any previous content
else:
self.entry=Entry()
self.entry.grid(column=1,row=i)
self.entries.append(self.entry)
# either way:
self.entry.insert(0,value)
Although this still breaks if you hit the open button twice since you do not reset self.values_for_entry_dictionary when opening a file, so the values in the file are added to any already open Entries so it would be a good idea to also reset it when opening a file:
def Open_button_command(self):
self.values_for_entry_dictionary=[]
...
If you wanted help with a working but buggy code you might consider submitting it for review and I'd be happy to provide other tips but I think this is sufficient to at least get the example code working as expected.
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
The following code exhibits a problem I do not understand:
from Tkinter import *
root = Tk()
cheese_var = IntVar()
parrot_var = IntVar(value=1)
check_menu = Menu(tearoff=0)
check_menu.add_checkbutton(label="Cheese", variable=cheese_var)
check_menu.add_checkbutton(label="Parrot", variable=parrot_var)
count = 0
class Top():
def __init__(self):
global count
count += 1
self.tl = Toplevel(root)
Label(self.tl, text="Window " + str(count)).pack()
self.mb = Menubutton(self.tl, text="Push Me", bg='pink')
self.menu = Menu(self.mb, tearoff=0)
self.menu.add_cascade(label="Choices", menu=check_menu)
self.menu.add_command(label="New Window", command=new_top)
self.mb.config(menu=self.menu)
self.mb.pack()
def new_top():
Top()
Top()
root.mainloop()
The menu brought up by the menu button in the created top level window initially behaves as expected. Clicking on the New Window command there creates a new such window, which also behaves as expected. Indeed, as long as you keep creating new top level windows, everything continues to work as expected. However, once you delete (close) any one of those windows, then, in a subsequently created new window, the Choices cascade on the new menu is not functional. (It is still OK in the windows created before the closing of one.)
The situation in which I initially encountered this symptom was much more complex, but I was able to simplify it down to the above example which exhibits the issue. I have discovered that I can avoid the problem by having each instance of Top create its own check_menu as an attribute; but I do not understand why this should be necessary. Please point me the way if there is one to avoid the problem without such replication of a cascade menu used in multiple windows.
Unfortunately, I don't think it is possible to do what you want. I'll try to explain as best as I can.
When you first run the script, check_menu is created and works fine for the first window. As you create more windows, check_menu is simply shared between them. However, when you close one of them, check_menu (and everything under it) is destroyed. So, when you create a new window after that, check_menu no longer exists and it doesn't show.
However, the script doesn't throw an error because, for some reason, Tkinter allows you to assign menus to things that aren't menus. Believe it or not, none of the following code:
self.menu.add_cascade(label="Choices", menu=None)
self.menu.add_cascade(label="Choices", menu=1)
self.menu.add_cascade(label="Choices", menu="")
will break the script. Each line simply does nothing but create an empty cascade "Choices".
That is basically what is happening. After closing one window, check_menu and everything under it is destroyed. Yet, Tkinter doesn't throw an error but instead assigns a menu to something that is no longer a menu (as far as what it is assigning the menu to, I believe it is using the old instance of check_menu, which was destroyed).
To solve this problem, recreate check_menu and everything under it each time you call Top. In other words, put the code for check_menu (and its options) in the __init__ method of Top. That way, each time Top is called, check_menu will exist.
Hope this helps (and that I explained it sufficiently :)