Maya Python Playback Create object each time a frame is played - python

Update :)
I have a UI with 2 buttons that control playback, "play" and "stop".
I defined a CustomLocator object. When the playback is running after pressing Start button, I would like to create and draw a customLocator each time a frame is played. Right now the locator is drawn when pressing the start button. But it's not drawn each time a frame is played and I can' t figure how to do it...thanks for your help
class PlayBackDoSomething():
def __init__(self):
self.drawUI()
self.state = False
def drawUI(self):
if pm.window("UI_MainWindow", exists = True):
pm.deleteUI("UI_MainWindow")
pm.window("UI_MainWindow", title = "test playback...", w = 150, h = 150, mnb = False, mxb = False, sizeable = True)
pm.columnLayout("UI_MainLayout", w = 150, h =300)
pm.button("UI_pbStartButton", label = "Start", w = 150, command=self.pbStart)
pm.button("UI_pbStopButton", label = "Stop", w = 150, command=self.pbStop)
pm.showWindow("UI_MainWindow")
def pbStart(self,*args):
pm.play(state=True)
self.state = True
self.doTheJob()
def pbStop(self,*args):
pm.play(state=False)
self.state = False
self.stopTheJob()
def doTheJob(self):
#this is basically part of code that i want to execute each time a frame is updated after pressing Play button
print ("start of job")
a = pm.keyframe(query = True, timeChange = True)
print a
t = pm.currentTime()
cl = CustomLocator(t, 0,0)
cl.draw()
def stopTheJob(self):
print ("end of job")
class CustomLocator():
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def draw(self):
pm.spaceLocator( p=(self.x, self.y, self.z) )
pm.spaceLocator( p=(self.x, self.y, self.z-5) )
pm.spaceLocator( p=(self.x, self.y, self.z+5) )
def main():
pb = PlayBackDoSomething()
main()

You can call your print by putting
print (time);
into an expression on an object in your scene. I'd only do this for debugging, since prints will slow down the interactive performance of the scene while you're working on it. If needed, you can add the expression to something like a camera when the start button is pushed and then remove it when you're done
update
You can do the same thing for creating locators (again, I recommend dynamically creating and removing the expression when playback stops or is complete. Here's a dumb example that creates locators as a cube moves around on playback
pCube1.translateX = sin(time);
float $tx = pCube1.translateX;
float $ty = pCube1.translateY;
float $tz = pCube1.translateZ;
spaceLocator -p $tx $ty $tz;
You could also just force update the time using cmds.currentTime() and do your process for every frame. That's much easier for traditional programming tasks than shoehorning this into an expression.

Related

Tkinter widgets created in an Update function run by tk.after function do not create untill the aforementioned Update ends

I intend to make a Py code which creates a tkinter dot that turns on a key press and deletes on a key press of couple keys.
The dot already is functional but i need it switch on and off on certain keypresses/mouse clicks which means i need an outside tkinter.mainloop() Update function.
The Update function with a while in it to constantly check if conditions to turn it off/on are present. But the Tkinter widget Somehow gets applied to the screen Only when the function nds. Like widget could be created but it will only take effect when function ends. And i need to turn it off/on dynamically.
I have tried to use a tkinter.after() with additional one at the end of called function only to find out an error of Recursion depth. What i expected to happen was that the function would be called over and over again, instead it runs that function like a while loop. I also have tried Asyncio.run() but it would result not making it visible till the function ends at least once. And I need to change it dynamically.
from tkinter import *
from tkinter import Canvas
from winsound import Beep
from time import sleep
import asyncio
import keyboard
import mouse
root = Tk()
width = root.winfo_screenwidth()
height = root.winfo_screenheight()
class tk_Dot():
def __init__(self,x=-1,y=-1,radius=4,color="red"):
self.x = x
if x == -1:
self.x = width/2-radius//2
print(self.x)
self.y = y
if y == -1:
self.y = height/2+radius//2
print(self.y)
self.radius=radius
self.color = color
self.lines = []
self.count = 1
def line(self,i):
return canvas.create_line(self.x, self.y-i, self.x+self.radius, self.y-i, fill=self.color)
def create(self):
self.lines = []
for i in range(0,self.radius):
self.lines.append(self.line(i))
def delete(self):
for i in range(0,self.radius):
canvas.delete(self.lines[i])
canvas.dtag(self.lines[i])
opacity_of_tk_window = 1 # From 0 to 1 0 meaning completely transparent 1 meaning everything created in canvas will give the color it was given
root.attributes('-alpha',opacity_of_tk_window)
# Invisible Tkinter window label
root.overrideredirect(True)
# Makes Transparent background
transparent_color = '#f0f0f0'
root.wm_attributes('-transparent', transparent_color)
canvas = Canvas()
# Rectangle filled with color that is specified above as the transparent color so practically making transparent background
canvas.create_rectangle(0, 0, width, height, fill=transparent_color)
canvas.pack(fill=BOTH, expand=1)
radius = 2
radius = 1+radius\*2
# Create a dot class
game_dot = tk_Dot(width/2-radius//2+1,height/2+1+radius//2,radius,"Red")
# Create a Dot at the middle of the calorant crosshair
# game_dot.create()
# Delete the dot
# game_dot.delete()
def Update():
game_dot.create()
print("Dot should be visible by now")
print("Is it?")
sleep(5) #sec
print("Oh yeah after the function ends.") # the problem
def Delete():
game_dot.delete()
root.geometry('%dx%d+%d+%d' % (width, height, -2,-2))
# Tkinter window always on top
root.attributes('-topmost',True)
root.after(1000,Update())
root.mainloop()

How to return a value while waiting for mouse interaction in PyQtGraph in GUI Application

I'd like to use a InfiniteLine in in pyqtgraph that moves with the mouse moving. Upon left-clicking the function shall return the xposition. I am able to retrieve the x-value on left-clicking and moving the InfiniteLine with the mouse movement. However I am stucked at simply returning the x position. I'd like to wait for the left click, as I want to repeat this procedure multiple times.
def addVerticalLineAndGetXOnClick(self):
def changePosVertLine(event):
if self.sceneBoundingRect().contains(event):
mousePoint = self.plotItem.vb.mapSceneToView(event)
line.setPos(mousePoint.x())
def onMouseClick(event):
if event.button() == 1:
self.removeItem(line)
self.scene().sigMouseClicked.disconnect()
self.setMouseTracking(False)
mousePoint = self.plotItem.vb.mapSceneToView(event.scenePos())
self.xPos.emit(mousePoint.x())
penLin = mkPen(color = '#000000', width = 1)
line = LRI(0.,pen = penLin, name= 'singleLineToGetXPos' )
self.addItem(line)
self.setMouseTracking(True)
self.scene().sigMouseMoved.connect(changePosVertLine)
self.scene().sigMouseClicked.connect(onMouseClick)
#wait to return after mouse click and return
"""???"""

Avoid multiple windows of the same kind

My goal is to have an interactive bar chart. I want to open a window containing a text. This shall happen when the user clicks on one of the bars. I started playing around a bit. In the following example, if you click on the left bar a window containing a text pops up. The problem I have, I only want to have the window opening once. So if you click a second time on the left bar, I don't want to open a second window. Therefore my question, how can I check if the window already exists and avoid multiple windows of the same kind. I already found a post regarding this topic, but I do not understand the solution, which was only explained poorly.
Thank you very much for your help.
def on_press(event):
cont,att = rect[0].contains(event)
if cont == True:
win = tk.Tk()
label1 = ttk.Label(win, text ="Test1").grid(column=0,row=0)
label2 = ttk.Label(win, text ="Test2").grid(column=0,row=1)
label3 = ttk.Label(win, text ="Test3").grid(column=0,row=2)
fig = plt.figure()
ax = fig.add_subplot(111)
x = [1,2,3]
y = [10,20,5]
rect = ax.bar(x,y)
test = rect[0].figure.canvas.mpl_connect('button_press_event', on_press)
plt.show()
Meanwhile I found a solution for my problem. Like MediaEU said, I use a variable which I set to true or false. However, one important "trick" is to use the method state().
If a main window is open, state() returns 'normal'. If a main window is closed state() throws an exception. Took me - as a starter - quite some time to figure it out.
Here is my suggestion:
class info_window:
def __init__(self, rect): # has an argument rect, for a specific bar in your plot
self.rect = rect
self.win_open = False
self.state = 'normal'
self.temp = self.rect.figure.canvas.mpl_connect('button_press_event', self)
def label(self):
self.label1 = ttk.Label(self.win, text ="Test1").grid(column=0,row=0)
self.label2 = ttk.Label(self.win, text ="Test2").grid(column=0,row=1)
self.label3 = ttk.Label(self.win, text ="Test3").grid(column=0,row=2)
def __call__(self, event):
cont, att = self.rect.contains(event)
try: # here is the "trick", in case window was closed self.win.state() throws an exception
self.win.state()
except: # setting win_open to False ensures that a window can be opened again
self.win_open = False
if cont == True:
if self.win_open == False: # if window does not exist, create it
self.win = tk.Tk()
self.label()
self.win_open = True # as long as window is open, you can't create it again

Activity Indicator in Tkinter?

Each OS has an activity indicator. OS X and iOS has a flower that lights up and fades each pedal in a circular pattern. Windows has a spinning blue disk thing. Android has a gray disk thing (I think it can be in a variety of other colors, too - IDK, I don't use Android much.)
What's the best way to use these icons in Tkinter? Is there some built in widget that provides this? Is there maybe a variable that I can point an Image widget at to get it to display this icon (and animate it?)
I know Tkinter provides a Progress Bar, which has an indeterminate mode. I don't want to use that - I need something that fits in a small square area, not a long rectangular area. The activity indicator as described in the first paragraph will be perfect.
Or is my best option going to be to just roll my own canvas animation?
This should be enough for what you trying to do. I would than create my own animation or download from the internet frame by frame which you want to display. Canvas are more for interactivity with the user thats why I am using a label for displaying the images...
import tkinter as tk
class Activity(tk.Label):
def __init__(self, master = None, delay = 1000, cnf = {}, **kw):
self._taskID = None
self.delay = delay
return super().__init__(master, cnf, **kw)
# starts the animation
def start(self):
self.after(0, self._loop)
# calls its self after a specific <delay>
def _loop(self):
currentText = self["text"] + "."
if currentText == "....":
currentText = ""
self["text"] = currentText
self._taskID = self.after(self.delay, self._loop)
# stopps the loop method calling its self
def stop(self):
self.after_cancel(self._taskID)
# Depends if you want to destroy the widget after the loop has stopped
self.destroy()
class AcitivityImage(Activity):
def __init__(self, imagename, frames, filetype, master = None, delay = 1000, cnf = {}, **kw):
self._frames = []
self._index = 0
for i in range(frames):
self._frames.append(tk.PhotoImage(file = imagename+str(i)+'.'+str(filetype)))
return super().__init__(master, delay, cnf, **kw)
def _loop(self):
self["image"] = self._frames[self._index]
# add one to index if the index is less then the amount of frames
self._index = (self._index + 1 )% len(self._frames)
self._taskID = self.after(self.delay, self._loop)
root = tk.Tk()
root.geometry("500x500")
# create a activity image widget
#root.b = AcitivityImage("activity", 3, "png", root)
#root.b.pack()
# create the activity widget
root.a = Activity(root, 500, bg = "yellow")
root.a.pack()
# start the loop
root.a.start()
#root.b.start()
# stop the activity loop after 7 seconds
root.after(7000, root.a.stop)
#root.after(8000, root.b.stop)
root.mainloop().

Creating two button methods Python

I am creating a program in Python that creates a shape based on user input. I need to create two functions to create buttons using Zeller's graphic.py file. One button needs to say Quit and the second needs to say Process. Here is what i have so far but as you can see, they are not in defined functions:
#create Quit button
quitButton = Text(Point(70,73), "Quit")
quitButton.draw(w)
Rectangle(Point(45, 50), Point(95,97)).draw(w)
#create Process button
enterButton = Text(Point(145,73), "Process")
enterButton.draw(w)
Rectangle(Point(120, 48), Point(170,98)).draw(w)
Here is a description of the necessary methods
createButton(text, pt1button, pt2button, win) creates a rectangle with corner points pt1button and pt2button with centered text in window win
clickedButton(button, clickPt) returns true/false if clickPt is in button.
I tried to create the function and received the following error.
Here is my function:
def createButton(text, pt1button, pt2button, win):
button = Text(Point(pt1button, pt2button), text)
button.draw(win)
Here is where I called the function:
createButton("Process",145,73,win)
createButton("Quit",70,73,win)
Here is the error that was thrown:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/project4FINAL.p‌​y", line 77, in <module> main()
File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/project4FINAL.p‌​y", line 27, in main buttonCreate("Process", 145,73, win)
NameError: global name 'win' is not defined
Any suggestions?
So from looking at the code it looks like you want to create a callback function for each button and then assign each to the canvas via the setMouseHandler of method of GraphWin.
So from the example given in the API:
from graphics import *
def example_callback():
print "I love unicorns!"
def main():
# win is a Canvas with a setMouseHandler method
win = GraphWin("My Circle", 100, 100)
c = Circle(Point(50,50), 10)
c.draw(win)
#Add a callback to the canvas
c.cavas.setMouseHandler(example_callback)
# or win.setMouseHandler(example_callback)
win.getMouse() # Pause to view result
win.close() # Close window when done
main()
Unless you have bounds checking in you callbacks (to see which shape on the canvas the mouse is inside), you should only have one canvas per drawn shape.
An example following the use of a createButton function:
def createButton(text, pt1button, pt2button, win):
button = Text(Point(pt1button, pt2button), text)
button.draw(win)
return button
def _callback(pt):
print "I love unicorns!"
print
print "Mouse clicked at x=%d, y=%d"%(pt.x,pt.y)
print
def test():
win = GraphWin()
win.setCoords(0,0,100,100)
quitButton = createButton("Quit",70,73,win)
Rectangle(Point(45, 50), Point(95,97)).draw(win)
win.setMouseHandler(_callback)
while True:
win.getMouse()
win.close()
Below is a complete example using a new Button object:
from graphics import *
class Button(object):
def __init__(self, text, text_pos, rect_pos, win, callback):
self.win = win
self.text = Text(text_pos, text)
self.text.draw(self.win)
# the limits of the button will be defined by the rectangle
self.coords = [rect_pos[0].x,rect_pos[0].y,rect_pos[1].x,rect_pos[1].y]
self.rect = Rectangle(*rect_pos)
self.rect.draw(self.win)
# set the buttons callback
self.callback = callback
def _is_inside(self,click):
limits = self.coords
return (limits[0] < click.x < limits[2]) and (limits[1] < click.y < limits[3])
class MyWindow(object):
def __init__(self,coords=(0,0,100,100)):
self.win = GraphWin()
self.win.setCoords(*coords)
# a list of all possible buttons
self.buttons = []
# register the master callback function
self.win.setMouseHandler(self._callback)
self._quit = False
#create a quit and confess button with a custom create method
self.create_button("Quit",(Point(10,10),Point(40,40)),Point(20,20),self.quit)
self.create_button("Confess",(Point(50,50),Point(80,80)),Point(60,60),self.confess)
def create_button(self,text,coords,text_coords,callback):
button = Button(text,text_coords,coords,self.win,callback)
# create a button and add it to our list of buttons
self.buttons.append(button)
def confess(self,point):
print
print "I love unicorns!"
print
def quit(self,point):
self._quit = True
self.win.close()
# This function is called for any click on the canvas
def _callback(self,point):
# Need to do a coordinate transform here to get the click in the coordinates of the button
x,y = self.win.trans.world(point.x,point.y)
point = Point(x,y)
print "Clicked at x=%d, y=%d"%(point.x,point.y)
# cycle through buttons and execute all for which the clicked point lies inside their rectangle
for button in self.buttons:
if button._is_inside(point):
button.callback(point)
# a loop to keep getting mouse input
def run(self):
while not self._quit:
try:
self.win.getMouse()
except GraphicsError:
break
if __name__ == "__main__":
x = MyWindow()
x.run()

Categories