VTK update position of multiple render windows - python

I'm running into a bit of a problem when trying to run multiple render windows in a Python VTK application I'm writing. The application is an attempt to render a 3D model in two separate views for a stereo application (i.e. Left render and right render), but I'm having an issue with updating the cameras of each window simultaneously. I currently have two nearly identical pipelines set up, each with its own vtkCamera, vtkRenderWindow, vtkRenderer, and vtkRenderWindowInteractor, the only difference being that the right camera is positionally shifted 30 units along the X axis.
Each of the render window interactors is being updated via the vtkRenderWindowInteractor.AddObserver() method that calls a simple function to reset the cameras to their original positions and orientations. The biggest issue is that this only seems to occur on one window at a time, specifically the window in focus at the time. It's as if the interactor's timer just shuts off once the interactor loses focus. In addition, when I hold down the mouse (And thus move the camera around), the rendered image begins to 'drift', resetting to a less and less correct position even though I have hardcoded the coordinates into the function.
Obviously I'm very new to VTK, and much of what goes on is fairly confusing as so much is hidden in the backend, so it would be amazing to acquire some assistance on the matter. My code is below. Thanks guys!
from vtk import*
from parse import *
import os
import time, signal, threading
def ParseSIG(signum, stack):
print signum
return
class vtkGyroCallback():
def __init__(self):
pass
def execute(self, obj, event):
#Modified segment to accept input for leftCam position
gyro = (raw_input())
xyz = parse("{} {} {}", gyro)
#This still prints every 100ms, but camera doesn't update!
print xyz
#These arguments are updated and the call is made.
self.leftCam.SetPosition(float(xyz[0]), float(xyz[1]), float(xyz[2]))
self.leftCam.SetFocalPoint(0,0,0)
self.leftCam.SetViewUp(0,1,0)
self.leftCam.OrthogonalizeViewUp()
self.rightCam.SetPosition(10, 40, 100)
self.rightCam.SetFocalPoint(0,0,0)
self.rightCam.SetViewUp(0,1,0)
self.rightCam.OrthogonalizeViewUp()
#Just a guess
obj.Update()
return
def main():
# create two cameras
cameraR = vtkCamera()
cameraR.SetPosition(0,0,200)
cameraR.SetFocalPoint(0,0,0)
cameraL = vtkCamera()
cameraL.SetPosition(40,0,200)
cameraL.SetFocalPoint(0,0,0)
# create a rendering window and renderer
renR = vtkRenderer()
renR.SetActiveCamera(cameraR)
renL = vtkRenderer()
renL.SetActiveCamera(cameraL)
# create source
reader = vtkPolyDataReader()
path = "/home/compilezone/Documents/3DSlicer/SlicerScenes/LegoModel-6_25/Model_5_blood.vtk"
reader.SetFileName(path)
reader.Update()
# create render window
renWinR = vtkRenderWindow()
renWinR.AddRenderer(renR)
renWinR.SetWindowName("Right")
renWinL = vtkRenderWindow()
renWinL.AddRenderer(renL)
renWinL.SetWindowName("Left")
# create a render window interactor
irenR = vtkRenderWindowInteractor()
irenR.SetRenderWindow(renWinR)
irenL = vtkRenderWindowInteractor()
irenL.SetRenderWindow(renWinL)
# mapper
mapper = vtkPolyDataMapper()
mapper.SetInput(reader.GetOutput())
# actor
actor = vtkActor()
actor.SetMapper(mapper)
# assign actor to the renderer
renR.AddActor(actor)
renL.AddActor(actor)
# enable user interface interactor
renWinR.Render()
renWinL.Render()
irenR.Initialize()
irenL.Initialize()
#Create callback object for camera manipulation
cb = vtkGyroCallback()
cb.rightCam = cameraR
cb.leftCam = cameraL
renWinR.AddObserver('InteractionEvent', cb.execute)
renWinL.AddObserver('InteractionEvent', cb.execute)
irenR.AddObserver('TimerEvent', cb.execute)
irenL.AddObserver('TimerEvent', cb.execute)
timerIDR = irenR.CreateRepeatingTimer(100)
timerIDL = irenL.CreateRepeatingTimer(100)
irenR.Start()
irenL.Start()
if __name__ == '__main__':
main()
EDIT:
Upon further viewing it seems like the TimerEvents aren't firing more than once in a row after a MouseClickEvent and I have no idea why.
EDIT 2: Scratch that, they are most definitely firing as per some test outputs I embedded in the code. I modified the code to accept user input for the self.leftCam.SetPosition() call within the vtkGyroCallback.execute() method (Thus replacing the hardcoded "10, 40, 100" parameters with three input variables) then piped the output of a script that simply displayed three random values into my main program. What this should have accomplished was having a render window that would constantly change position. Instead, nothing happens until I click on the screen, at which point the expected functionality begins. The whole time, timer events are still firing and inputs are still being accepted, yet the cameras refuse to update until a mouse event occurs within the scope of their window. What is the deal?
EDIT 3: I've dug around some more and found that within the vtkObject::InvokeEvent() method that is called within every interaction event there is a focus loop that overrides all observers that do not pertain to the object in focus. I'm going to investigate if there is a way to remove focus so that it will instead bypass this focus loop and go to the unfocused loop that handles non focused objects.

So the solution was surprisingly simple, but thanks to the lack of quality documentation provided by VTK, I was left to dig through the source to find it. Effectively all you have to do is pseudo-thread Render() calls from each of the interactors via whatever callback method you're using to handle your TimerEvents. I did this using ID properties added to each interactor (seen in code provided below). You can see that every time a TimerEvent is fired from the irenR interactor's internal timer (irenR handles the right eye), the irenL's Render() function is called, and vice versa.
To solve this I first realized that the standard interactor functionalities (Mouse events and the like), worked normally. So I dug around the source in vtkRenderWindowInteractor.cxx and realized that those methods were abstracted to the individual vtkInteractorStyle implementations. After rooting around in the vtkInteractorStyleTrackball.cxx source, I found that there was actually a Render() function within the vtkRenderWindowInteractor class. Go figure! The documentation sure didn't mention that!
Unfortunately, two renders at once is actually very slow. If I do this method with just one window (At which point it becomes unnecessary), it runs wonderfully. Framerate tanks with a second window though. Oh well, what can you do?
Here's my corrected code (Finally I can start working on what I was supposed to be developing):
from vtk import*
from parse import *
import os
import time, signal, threading
def ParseSIG(signum, stack):
print signum
return
class vtkGyroCallback():
def __init__(self):
pass
def execute(self, obj, event):
#Modified segment to accept input for leftCam position
gyro = (raw_input())
xyz = parse("{} {} {}", gyro)
#print xyz
# "Thread" the renders. Left is called on a right TimerEvent and right is called on a left TimerEvent.
if obj.ID == 1 and event == 'TimerEvent':
self.leftCam.SetPosition(float(xyz[0]), float(xyz[1]), float(xyz[2]))
self.irenL.Render()
#print "Left"
elif obj.ID == 2 and event == 'TimerEvent':
self.rightCam.SetPosition(float(xyz[0]), float(xyz[1]), float(xyz[2]))
self.irenR.Render()
#print "Right"
return
def main():
# create two cameras
cameraR = vtkCamera()
cameraR.SetPosition(0,0,200)
cameraR.SetFocalPoint(0,0,0)
cameraL = vtkCamera()
cameraL.SetPosition(40,0,200)
cameraL.SetFocalPoint(0,0,0)
# create a rendering window and renderer
renR = vtkRenderer()
renR.SetActiveCamera(cameraR)
renL = vtkRenderer()
renL.SetActiveCamera(cameraL)
# create source
reader = vtkPolyDataReader()
path = "/home/compilezone/Documents/3DSlicer/SlicerScenes/LegoModel-6_25/Model_5_blood.vtk"
reader.SetFileName(path)
reader.Update()
# create render window
renWinR = vtkRenderWindow()
renWinR.AddRenderer(renR)
renWinR.SetWindowName("Right")
renWinL = vtkRenderWindow()
renWinL.AddRenderer(renL)
renWinL.SetWindowName("Left")
# create a render window interactor
irenR = vtkRenderWindowInteractor()
irenR.SetRenderWindow(renWinR)
irenL = vtkRenderWindowInteractor()
irenL.SetRenderWindow(renWinL)
# mapper
mapper = vtkPolyDataMapper()
mapper.SetInput(reader.GetOutput())
# actor
actor = vtkActor()
actor.SetMapper(mapper)
# assign actor to the renderer
renR.AddActor(actor)
renL.AddActor(actor)
# enable user interface interactor
renWinR.Render()
renWinL.Render()
irenR.Initialize()
irenL.Initialize()
#Create callback object for camera manipulation
cb = vtkGyroCallback()
cb.rightCam = renR.GetActiveCamera()#cameraR
cb.leftCam = renL.GetActiveCamera()#cameraL
cb.irenR = irenR
cb.irenL = irenL
irenR.ID = 1
irenL.ID = 2
irenR.AddObserver('TimerEvent', cb.execute)
irenL.AddObserver('TimerEvent', cb.execute)
timerIDR = irenR.CreateRepeatingTimer(100)
timerIDL = irenL.CreateRepeatingTimer(100)
irenL.Start()
irenR.Start()
if __name__ == '__main__':
main()

Related

Looking for a simple window renderer for Python

I'm currently looking for a simple step-by-step window renderer library for Python.
The main idea behind what I need is that I want to be able to change the input data before I call the window render function, by doing so the window would render a moving point with some additional static points. The rendered window would refresh while the program is running. For example:
# Some points
data_points_x = [2,4,6,11,22]
data_points_y = [5,-1,23,41,1]
window.add(x, y, style="point")
# Another point
important_point_x = 23
important_point_y = 13
window.add(important_point_x, important_point_y, style="star")
# Main rendering loop
while True:
# Move one point
important_point_x = important_point_x + 1
window.render()
I know matplotlib can do something similar, but I'm interested if there is any other library capable of doing more, for example, rendering text, lines and so on. Finally, pyplot does not refresh the window when calling plot.show() when the program is running.

How to create many instances of vtkContourWidget

I want to be able to create many instances of vtkContourWidget, letting the user draw lines and manipulating the nodes, but it seems I'm not doing it right.
Here is the code:
import vtk
def main():
# Create a renderer, render window, and interactor
renderer = vtk.vtkRenderer()
renderWindow = vtk.vtkRenderWindow()
renderWindow.AddRenderer(renderer)
Interactor = vtk.vtkRenderWindowInteractor()
Interactor.SetRenderWindow(renderWindow)
style = vtk.vtkInteractorStyleTerrain()
Interactor.SetInteractorStyle(style)
Interactor.AddObserver("KeyPressEvent", keyPressEvent)
# Render and interact
renderWindow.Render()
Interactor.Start()
def keyPressEvent(obj, event):
key = obj.GetKeySym()
if key == 'n':
contourRep = vtk.vtkOrientedGlyphContourRepresentation()
contourWidget = vtk.vtkContourWidget()
contourWidget.SetInteractor(obj)
contourWidget.SetRepresentation(contourRep)
contourWidget.On()
#contourWidget.SetEnabled()
obj.Start()
return
main()
It almost works fine this way, the problem is that, when I want to close the application window, I have to click the button to close the window many times. And I have to click one time for each vtkContourWidget created.
It seems that each time I call the function keyPressEvent and create a vtkContourWidget, the obj.Start() (that is the same of Interactor.Start()) line instantiates kind of another instance of the application?
I've also tried contourWidget.SetEnabled() (with obj.Start() commented out), and it works to create new instances of vtkContourWidget, but when I try to close the app window, the app freezes.
With both obj.Start() and contourWidget.SetEnabled() on the code, I can create many instances, but each time I create a new instance, the previous one disappears.
I think this topic may have the solution, but I don't know how to implement it.
Just copy-pasting your code and running it with VTK 6.3 on Ubuntu 16.04 seems to work correctly. The window closes as expected using the closing button or "q".
So this seems to be a bug related to the VTK release you are using on your system.
Kind regards.

Getting, Storing, Setting and Modifying Transform Attributes through PyMel

I'm working on something that gets and stores the transforms of an object moved by the user and then allows the user to click a button to return to the values set by the user.
So far, I have figured out how to get the attribute, and set it. However, I can only get and set once. Is there a way to do this multiple times within the script running once? Or do I have to keep rerunning the script? This is a vital question for me get crystal clear.
basically:
btn1 = button(label="Get x Shape", parent = layout, command ='GetPressed()')
btn2 = button(label="Set x Shape", parent = layout, command ='SetPressed()')
def GetPressed():
print gx #to see value
gx = PyNode( 'object').tx.get() #to get the attr
def SetPressed():
PyNode('object').tx.set(gx) #set the attr???
I'm not 100% on how to do this correctly, or if I'm going the right way?
Thanks
You aren't passing the variable gx so SetPressed() will fail if you run it as written(it might work sporadically if you tried executing the gx = ... line directly in the listener before running the whole thing -- but it's going to be erratic). You'll need to provide a value in your SetPressed() function so the set operation has something to work with.
As an aside, using string names to invoke your button functions isn't a good way to go -- you code will work when executed from the listener but will not work if bundled into a function: when you use a string name for the functions Maya will only find them if they live the the global namespace -- that's where your listener commands go but it's hard to reach from other functions.
Here's a minimal example of how to do this by keeping all of the functions and variables inside another function:
import maya.cmds as cmds
import pymel.core as pm
def example_window():
# make the UI
with pm.window(title = 'example') as w:
with pm.rowLayout(nc =3 ) as cs:
field = pm.floatFieldGrp(label = 'value', nf=3)
get_button = pm.button('get')
set_button = pm.button('set')
# define these after the UI is made, so they inherit the names
# of the UI elements
def get_command(_):
sel = pm.ls(sl=True)
if not sel:
cmds.warning("nothing selected")
return
value = sel[0].t.get() + [0]
pm.floatFieldGrp(field, e=True, v1= value[0], v2 = value[1], v3 = value[2])
def set_command(_):
sel = pm.ls(sl=True)
if not sel:
cmds.warning("nothing selected")
return
value = pm.floatFieldGrp(field, q=True, v=True)
sel[0].t.set(value[:3])
# edit the existing UI to attech the commands. They'll remember the UI pieces they
# are connected to
pm.button(get_button, e=True, command = get_command)
pm.button(set_button, e=True, command = set_command)
w.show()
#open the window
example_window()
In general, it's this kind of thing that is the trickiest bit in doing Maya GUI -- you need to make sure that all the functions and handlers etc see each other and can share information. In this example the function shares the info by defining the handlers after the UI exists, so they can inherit the names of the UI pieces and know what to work on. There are other ways to do this (Classes are the most sophisticated and complex) but this is the minimalist way to do it. There's a deeper dive on how to do this here

Python if statement not working as expected. Code 'apparently' called twice

I have this piece of code:
import bge
import GameLogic
import os
os.system("cls")
scene = GameLogic.getCurrentScene()
objects = scene.objects
objectCube = objects["Cube"]
visible = objectCube.visible
if visible == True:
objectCube.setVisible(False, True)
else:
objectCube.setVisible(True, True)
This code is supposed to toggle the visibility of an object but instead the object disappears and immediately reappears in a split second. It looks as if it just flickers. What am I doing wrong?
Also, don't worry about the other variables, they work fine. I have tested them using some Console outputs.
The problem: The mouse sensor sends two signals per click, one for mouse down and one for mouse up. Mouse down sends a positive signal while mouse up sends negative.
You can test this by holding the mouse button down, the cube will disappear and when you release the mouse it will return.
The solution: Use the sensor's positive property to determine if this is a mouse up or down event.
import bge
import GameLogic
import os
os.system("cls")
scene = GameLogic.getCurrentScene()
objects = scene.objects
objectCube = objects["Cube"]
visible = objectCube.visible
# get the mouse sensor
cont = bge.logic.getCurrentController()
sens = cont.sensors['Mouse']
if sens.positive: # positive means a down button event
if visible == True:
objectCube.setVisible(False, True)
else:
objectCube.setVisible(True, True)
The second parameter of setVisible set the game children objects visibility. You set it to True. In this case You hidden the main object and show the children objects.
Following to http://bgepython.tutorialsforblender3d.com/GameObject/setVisible recursive parameter does not mean recursive show/hide all children elements but set the visibility to children elements to True/False
Following to http://www.tutorialsforblender3d.com/BGE_Python/Sensors/Mouse/MouseSensor_LButton_getButtonStatus.html mouse event send two events mouse press and mouse release. Maybe You do not differentiate between press and release and call code twice?

Importing tkinter button from separate module

We have a functioning program that uses Tkinter as its GUI. Everything works fine however different branches of the code are now using different hardware which realistically need different buttons. Hence we'd like to have the main GUI import modules representing the buttons depending on what hardware is being used.
I've cut out some of the code below, I'm interested in removing the makemenu() function to a separate module, hence when it is called in the Application __init__ (self.makemenu(master)) I would like to make that a reference to a separate module. I've tried doing this and am having trouble. Is this even possible?
I'm a little confused on the parent structure, what needs to be passed to my button module, etc.? I know this is a poorly constructed question but if anyone is able to advise if this is possible and put my on the right track that would be great. For example if someone could show how to modify this code to have the buttons defined in a separate module I could figure out how to do the same in my module.
# Import necessary libraries
import sys
import os
import Tkinter as tk
class Application(tk.Frame):
##################################################################
## Final functions are designed to initialize the GUI and
## connect various mouse movements to useful functions.
##################################################################
def definevars(self):
'''Original definition of all of the key variables that
we need to keep track of while running the GUI
'''
self.disable = True
self.savimgstatus = 'off'
self.mode = 'Standby'
self.status = 'Not Ready'
def makemenu(self,master):
''' Function to create the main menu bar across
the top of the GUI.
'''
self.menubar = tk.Menu(master)
## Motor Submenu
motormenu = tk.Menu(self.menubar,tearoff=1)
motormenu.add_command(label='ALT',state='disabled')
motormenu.add_command(label='error check',
command=lambda: self.geterror('alt'))
motormenu.add_separator()
motormenu.add_command(label='AZ',state='disabled')
motormenu.add_command(label='error check',
command=lambda: self.geterror('az'))
self.menubar.add_cascade(label='Tracker Motors',menu=motormenu)
## Set the big menu as the main menu bar.
master.config(menu=self.menubar)
def __init__(self,tcpconn,DOME,TRACKERSTAGE, master=None):
'''Main function to initialize the GUI. Will scale
the size of the GUI to fit any size screen... to a
point. It will not allow it to be smaller than
600x800.
'''
self.buf = 1024
## Check resolution of screen. Make GUI 2/3rds of size
## unless that means under 600x800.
fh = round(master.winfo_screenheight()*2./3.)
fw = round(master.winfo_screenwidth()*2./3.)
if fh < 600: fh = 600
if fw < 800: fw = 800
print 'GUI resolution set to {0} x {1}'.format(fw,fh)
self.fw = fw
self.fh = fh
self.imwidth = int(0.45*self.fw)
self.imheight = int(0.45*self.fh)
self.imcentx = self.imwidth/2
self.imcenty = self.imheight/2this
## Initialize Frame
tk.Frame.__init__(self, master, height=fh,width=fw)
self.grid()
self.grid_propagate(0)
## Initialize Various variables.
self.definevars()
## Create buttons, etc.
self.createWidgets()
self.makemenu(master)
self.disableall()
## Main Loop function
self.checkoutput()
###################################################################
# Initialize GUI window.
root = tk.Tk()
root.title('Hardware') # window title
app = Application(master=root)
app.mainloop() # go into the main program loop
sys.exit()
If you want to move makemenu to a separate module, that should be pretty simple. However, you'll need to change a few things.
Since makemenu no longer has a reference to self (or has a different reference, if you implement it as a separate class), you need to replace calls like command=lambda: self.geterror('alt')) to be command=lambda: master.geterror('alt')).
The other thing I recommend is to remove the call to add the menu to the root. I believe that modules shouldn't have side effects like this -- the function should make a menu and return it, and let the caller decide how to use it, ie:
self.menubar=makemenu(master)
master.configure(menu=self.menubar)
Roughly speaking, this is a variation of the MVC (model/view/controller) architectural pattern where the Application instance is your controller (and also part of the view unless you make modules of all your UI code). The menu is part of the view, and forwards UI functions to the controller for execution.
Your application then looks something like this:
from makemenu import makemenu
class Application(...):
def __init__(...):
...
self.menubar = makemenu(master)
master.config(menu=self.menubar)
...

Categories