PDA

View Full Version : does vizard handle input differently when it's embedded?


Adam.Grey
06-27-2012, 12:54 PM
Is there something special I have to do for vizard embedded to get mouse and keyboard?

I need to embed my vizard window in a WPF control. I adapted this tutorial (http://msdn.microsoft.com/en-us/library/ms752055(v=vs.90).aspx) and this one (http://forum.worldviz.com/showthread.php?t=1345)(thanks farshizzo) to suit and made a nice control that starts up a vizard script and passes the handle to the window I've created as the first paramater. The script, if it sees the first parameter, takes that to be a window handle and embeds itself in that window. It almost entirely works.
http://i.imgur.com/4D353.png
However, I can't seem to get input to go through. Microsoft's tutorial works just fine with their own listbox, but I'm not sure if vizard's input is handled differently.
I know the script itself is properly receiving input. If I run this script with the only difference being that it isn't embedded, it responds properly to mouse and keyboard input.
I know vizard isn't hanging or blocked out of input: The ball spins, and if I check the keyboard state manually (e.g., viz.key.isDown(' ', True)), I can see if keys are up or down. onkeydown doesn't seem to work, however.
p/invoking win32's setfocus does not appear to do anything.

here's the vizard script:
import viz
import vizact


viz.add('tut_ground.wrl')
ball = viz.add('ball.wrl',pos=(0,1.8,2))
ball.add(vizact.spin(0,1,0,90))

def mytimer(num):
if num is 0:
ball.setPosition(0, 1.8, 4)
elif num is 1:
if viz.key.isDown(' ', True):
ball.setPosition(0, 1.8, 3)

vizact.ontimer(0.1666, mytimer, 1)
vizact.ontimer(1.5, mytimer, 0)
vizact.onkeydown('b', ball.setPosition, [0,1.8,5])

if(len(sys.argv) > 1):
viz.go(viz.EMBEDDED, window=int(sys.argv[1]))
while True:
viz.updateframe()
else:
viz.go()and here's the vizard box control:using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace vizardbox
{
public partial class VizardBox : UserControl
{
public String VizardAdditionalStartupArguments
{
get { return (String)GetValue(VizardAdditionalStartupArgumentsP roperty); }
set { SetValue(VizardAdditionalStartupArgumentsProperty, value); }
}
public bool VizardConsole
{
get { return (bool)GetValue(VizardConsoleProperty); }
set { SetValue(VizardConsoleProperty, value); }
}
public String VizardScript
{
get { return (String)GetValue(VizardScriptProperty); }
set { SetValue(VizardScriptProperty, value); }
}
public String VizardInstallation
{
get { return (String)GetValue(VizardInstallationProperty); }
set { SetValue(VizardInstallationProperty, value); }
}
public static readonly DependencyProperty VizardAdditionalStartupArgumentsProperty = DependencyProperty.Register("VizardAdditionalStartupArguments", typeof(String), typeof(VizardBox), new UIPropertyMetadata(""));
public static readonly DependencyProperty VizardConsoleProperty = DependencyProperty.Register("VizardConsole", typeof(bool), typeof(VizardBox), new UIPropertyMetadata(false));
public static readonly DependencyProperty VizardScriptProperty = DependencyProperty.Register("VizardScript", typeof(String), typeof(VizardBox), new UIPropertyMetadata(""));
public static readonly DependencyProperty VizardInstallationProperty = DependencyProperty.Register("VizardInstallation", typeof(String), typeof(VizardBox), new UIPropertyMetadata("C:\\Program Files (x86)\\WorldViz\\Vizard4\\bin\\winviz.exe"));


vizHwndHost vh;
public VizardBox()
{
InitializeComponent();
}
public void Go()
{
vh = new vizHwndHost();
_hostborder.Child=vh;
vh.go(VizardScript, VizardInstallation, VizardAdditionalStartupArguments, VizardConsole);
}

public void Stop()
{
_hostborder.Child = null;
vh.stop();
}
}

public class vizHwndHost : HwndHost
{
IntPtr hwndVizardHandle;
Process vizardProc;
~vizHwndHost()
{
stop();
DestroyWindow(hwndVizardHandle);
}
public void stop()
{
if (vizardProc != null)
if(!vizardProc.HasExited)
vizardProc.Kill();
}
public void go(string script, string vizardLocation, string additionalStartupArguments, bool console)
{
vizardProc = new Process();
vizardProc.StartInfo.FileName = vizardLocation;
vizardProc.StartInfo.Arguments = (console? "-console" : "") + " \"" + script + "\" " + hwndVizardHandle.ToString() + " " + additionalStartupArguments;
vizardProc.Start();
}
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
hwndVizardHandle = IntPtr.Zero;
hwndVizardHandle = CreateWindowEx(0, "static", "",
WS_CHILD | WS_VISIBLE,
0, 0,
0, 0,
hwndParent.Handle,
(IntPtr)HOST_ID,
IntPtr.Zero,
0);
return new HandleRef(this, hwndVizardHandle);
}
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
handled = false;
return IntPtr.Zero;
}
public void setFocus()
{
SetFocus(hwndVizardHandle);
}
protected override void OnGotMouseCapture(MouseEventArgs e)
{
base.OnGotMouseCapture(e);
e.Handled = true;
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
DestroyWindow(hwnd.Handle);
}
//PInvoke declarations
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateWindowEx(int dwExStyle,
string lpszClassName,
string lpszWindowName,
int style,
int x, int y,
int width, int height,
IntPtr hwndParent,
IntPtr hMenu,
IntPtr hInst,
[MarshalAs(UnmanagedType.AsAny)] object pvParam);
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern IntPtr SetFocus(IntPtr hWnd);
internal const int
WS_CHILD = 0x40000000,
WS_VISIBLE = 0x10000000,
LBS_NOTIFY = 0x00000001,
HOST_ID = 0x00000002;
}
}

farshizzo
06-27-2012, 02:52 PM
I haven't embedded Vizard into a WPF application before, so I'm not too familiar with the subject. Can you verify that Vizard is receiving any Windows message? For example, if you resize the vizard window from within the WPF app, do you receive a viz.WINDOW_SIZE_EVENT or does Vizard adjust projection matrix automatically.

Also, I would suggest making a small change to your Vizard script. Your embedded update loop will never exit, since you are looping over True. Try the following instead:while not viz.done():
viz.frame()

This will allow the script to properly exit when viz.quit() is called or the window is destroyed.

Adam.Grey
06-28-2012, 08:35 AM
Actually I couldn't get vizard to respond to a resize. So I started searching for automatically resizing a hosted window and found a separate solution: involve winforms. Adapting this (http://stackoverflow.com/a/5070001/1173856), I was able to get what I needed. Many thanks for pointing me in the right direction.
It now resizes, responds to input, etc etc. This method is reliant on the main window handle of the vizard process, so I regrettably can't support the console in my nicely encapsulated vizard control. It has to wait for vizard to start up the main form, so there's a quick flash of vizard's graphics window out of place.
So to anyone looking to embed a win32 application in a WPF control, I'd say just use winforms.
and I guess to answer my own original question, "nope."

farshizzo
06-28-2012, 10:07 AM
I'm not sure this will work, but another method is to reparent the Vizard graphics window to the WPF window, instead of reusing the WPF window handle. This is what the Vizard Script -> Run Docked command does, and input messages still work properly.

Let the Vizard script create its own window as usual, but parent it to the handle passed to the command line:viz.go()

import win32gui
import win32con

if(len(sys.argv) > 1):

viz.window.setBorder(viz.BORDER_NONE)
viz.window.setPosition([0,0])

hwndChild = viz.window.getHandle()
hwndParent = int(sys.argv[1])

style = win32gui.GetWindowLong(hwndChild,win32con.GWL_STYL E)
style = style | win32con.WS_CHILD
style = style & ~(win32con.WS_OVERLAPPEDWINDOW | win32con.WS_POPUP)
win32gui.SetWindowLong(hwndChild,win32con.GWL_STYL E,style)

win32gui.SetParent(hwndChild ,hwndParent)


One important step with this method is that you will need to manually resize the Vizard graphics window when the parent window is resized. If you know the WPF control will always remain the same size, then you can hard code the size of the window in the Vizard script, or pass it as an argument.