PDA

View Full Version : Autoselecting viewport?


Gen-an
10-01-2008, 05:49 AM
Hi, does anyone know if it`s possible to autoselect a viewport by hovering the mouse above it without the need to press the right mouse button? Thanks

ZeBoxx2
10-01-2008, 03:49 PM
hmm... only with an initialization phase, looks like.. can't get the viewports' positions/dimensions arbitrarily.. only for the active one. (As far as I can tell)

So...

See further posts for updated code

You can easily replace the rollout + timer to a .NYET timer if you really want to - I like the dialogs because it makes it easy to kill the timer (just kill the dialog), whereas .NYET timers tend to linger around especially if you lose the pointer variable to them and thus no longer have an easy way to kill the buggers.

Edit: fix for < 4 viewports (d'oh)
Edit: Switching layouts fixed
Edit: 'Fixed' viewport resizing breaking things
Edit: 2D viewports fixed

scrimski
10-01-2008, 04:02 PM
Have alook here.
http://scripts.breidt.net/#shortcuts

I'm using this for viewport maximize and some other toggles.

Gen-an
10-01-2008, 09:00 PM
hmm... only with an initialization phase, looks like.. can't get the viewports' positions/dimensions arbitrarily.. only for the active one. (As far as I can tell)

So...


-- some variables to track
global viewportBoxes
global viewportLayout
global viewportCount

-- initialization function... get the positions/dimensions for each viewport
fn autoVPinit = (
viewportBoxes = #()
local currentVp = viewport.activeViewPort
for i = 1 to viewport.numViews do (
viewport.activeViewPort = i
local vpTopLeft = mouse.screenPos - mouse.pos
local vpSize = getViewSize()
append viewportBoxes (Box2 vpTopLeft.x vpTopLeft.y vpSize.x vpSize.y)
)
if (currentVp != 0) then ( viewport.activeViewPort = currentVp )
viewportLayout = viewport.getLayout()
viewportCount = viewport.numViews
)

-- destroy any existing autoVP dialog
if (classOf autoVP == RolloutClass) then ( destroyDialog autoVP )

-- define the new one
-- essentially, the timer checks every 100ms whether the mouse
-- is in any of the viewports' (initialized) rects, and switches to the one it's in, if any
rollout autoVP "autoVP" width:88 height:0 (
timer ticktock interval:100 active:true

on ticktock tick do (
local skip = false
-- better make sure that layout changes don't ruin things here
if (viewport.getLayout() != viewportLayout) then (
autoVPinit()
)
else if (viewport.numViews != viewportCount) then (
autoVPinit()
)
else (
-- ditto for viewport resizings
local currentVp = viewport.activeViewPort
if (currentVp != 0) then (
local currentVpAssumedBox = viewportBoxes[currentVp]
if (getViewSize() != [currentVpAssumedBox.w,currentVpAssumedBox.h]) then (
autoVPinit()
)
)
)

for i = 1 to viewport.numViews do (
viewportBox = viewportBoxes[i]
if (contains viewportBox mouse.screenPos) then (
viewport.activeViewPort = i
)
)
)
)
-- begin the action
autoVPinit()
createDialog autoVP


You can easily replace the rollout + timer to a .NYET timer if you really want to - I like the dialogs because it makes it easy to kill the timer (just kill the dialog), whereas .NYET timers tend to linger around especially if you lose the pointer variable to them and thus no longer have an easy way to kill the buggers.

Edit: fix for < 4 viewports (d'oh)
Edit: Switching layouts fixed
Edit: 'Fixed' viewport resizing breaking things
Edit: 2D viewports fixed

oh my god you`re a legend ZeBoxx2 !!! thanks so much works like a charm :beer: :bowdown:

scrimski: thanks for the link also mate but Zeboxx`s script is all I needed, thanks again guys

ZeBoxx2
10-01-2008, 11:20 PM
no prob :)

just checked the other script.. looks like that's more for being bound to a key, rather than automatically doing its trick.. also has to loop through all viewports each time and I *think* doesn't like 2D viewports... but most interestingly from that is that I guess they haven't found a way to get the rect for a viewport automagically either.. *eyes Autodesk wishlist*

scrimski
10-02-2008, 07:34 AM
just checked the other script.. looks like that's more for being bound to a key
The toggles are bound to keys, correct, but you just hover over the viewport and don't need to rightclick it anymore.

martinB
10-10-2008, 12:39 PM
Richard, nice solution, I'll store that away for future reference.

I personally prefer 'on mouse over' focus and your script handles it nicely for any hotkey and it seems still fast enough, even though it checks ever 100ms. For non-3dsmax Windows work, I use a small utility called KatMouse (http://ehiti.de/katmouse/) that has an option to set windows focus on scroll wheel. It does not always work for all applications but it has exceptions for those cases, too.

You probably could even speed up your script a tiny bit by checking the previously active viewportBox first and bailing out of that last 'for i' loop as soon as 'contains' is true for the first time?

Funny that the polling script still works even when 3ds Max is behind some other window or locked due to a modal dialog.

Cheers
-- MartinB

ZeBoxx2
10-10-2008, 06:19 PM
yeah, 100ms poll isn't too bad - a callback would be nicer, of course :)

And you're right - I actually had an earlier version (that used entirely too much code) that would start from the currently active viewport and loop over the rest from there, which did trap that "if it's still in the same window" bit, but that version ended up having some other problems so started afresh thinking there had to be a better way %)

I'll adjust it to that loop in a moment - coffee now ;)

ZeBoxx2
10-10-2008, 07:54 PM
figured I might as well spiffy it up further.. see the code for changes.. the big ones are macroscript definition and dotNetTimer. Note that the script should be in a startup or plugin (sub)folder, as the macroscript depends on the main body of code.


-- AutoVP - MouseOver Viewport Switcher
-- Richard Annema -- SplutterFish, LLC
--
-- v0.04
-- - trapping errors when called by the .NET Timer so that the thing can be halted right-quick.
--
-- v0.03
-- - fixed AutoVP not existing causing the macroscript to go boink.
-- - fixed layout change breaking the init routine
-- - made .NET Timer use a preference
--
-- v0.02
-- - structified
-- - slightly leaner in code run
-- - uses dotNetTimer in 3ds Max 9+
-- - added macroscript definition, typically to be used for a button to toggle AutoVP on/off (especially in 3ds Max 9+)
--
-- v0.01
-- - initial version

-- preference settings
global autoVP_interval = 100 -- milliseconds
global autoVP_useDotNetTimer = true -- if true, uses a .net timer in 3ds Max r9+

-- globals
global autoVP, autoVP_roll, autoVP_dotNyetInvoke

-- UI
if (classOf autoVP_roll == RolloutClass) then ( destroyDialog autoVP_roll )
rollout autoVP_roll "autoVP" width:64 height:32 (
local ui_isOpen = false

timer ticktock interval:100 active:true
checkbutton ckb_active "active" checked:true pos:[4,4] width:56 height:24 highlightColor:(color 192 255 192) tooltip:"Toggle AutoVP"

on ticktock tick do ( autoVP.setMouseActiveViewport() )

on ckb_active changed state do (
ticktock.active = state
updateToolbarButtons()
)

on autoVP_roll open do (
ui_isOpen = true
updateToolbarButtons()
ticktock.interval = autoVP_interval
autoVP.init()
)
on autoVP_roll close do (
ui_isOpen = false
updateToolbarButtons()
)
on autoVP_roll moved pos do ( autoVP.uiPos = pos )
)

-- Main struct with variables and functions
Struct AutoVP_s (
viewportBoxes,
viewportLayout,
viewportCount,
uiPos,
dotNetTimer = undefined,

-- initialization function... get the positions/dimensions for each viewport
fn init = (
viewportBoxes = #()
local currentVp = viewport.activeViewPort
for i = 1 to viewport.numViews do (
local index = (mod (i + currentVp) viewport.numViews) + 1
viewport.activeViewPort = index
local vpTopLeft = mouse.screenPos - mouse.pos
local vpSize = getViewSize()
viewportBoxes[index] = (Box2 vpTopLeft.x vpTopLeft.y vpSize.x vpSize.y)
)
if (currentVp != 0) then ( viewport.activeViewPort = currentVp )
viewportLayout = viewport.getLayout()
viewportCount = viewport.numViews
),

fn closeUI = (
if (classOf autoVP_roll == RolloutClass) then ( destroyDialog autoVP_roll )
),

fn showUI = (
-- destroy any existing autoVP dialog
if (classOf autoVP_roll == RolloutClass) then ( destroyDialog autoVP_roll )

-- begin the action, re-use last known position if available
if (uiPos == undefined) then ( createDialog autoVP_roll style:#(#style_toolwindow,#style_sysmenu) )
else ( createDialog autoVP_roll 64 32 uiPos.x uiPos.y style:#(#style_toolwindow,#style_sysmenu) )
),

fn getViewportUnderMouse = (
local skip = false
-- better make sure that layout changes don't ruin things here
if (viewport.getLayout() != autoVP.viewportLayout) then (
autoVP.init()
)
else if (viewport.numViews != autoVP.viewportCount) then (
autoVP.init()
)
else (
-- ditto for viewport resizings
local currentVp = viewport.activeViewPort
if (currentVp != 0) then (
local currentVpAssumedBox = autoVP.viewportBoxes[currentVp]
if (getViewSize() != [currentVpAssumedBox.w,currentVpAssumedBox.h]) then (
autoVP.init()
)
)
)

local result = undefined
for i = 1 to viewport.numViews do (
viewportBox = autoVP.viewportBoxes[i]
if (contains viewportBox mouse.screenPos) then (
result = i
exit
)
)
result
),

fn setMouseActiveViewport = (
local hoverView = getViewportUnderMouse()
if ((hoverView != undefined) AND (hoverView != viewport.activeViewport)) then (
viewport.activeViewPort = hoverView
)
)
)
autoVP = autoVP_s()

-- .NET events can't invoke functions that are within Structs properly ... so here's an intermediating function >_<
fn autoVP_dotNyetInvoke = (
try (
autoVP.setMouseActiveViewport()
)
catch (
autoVP.dotNetTimer.stop()
local err = getCurrentException()
messageBox ("An error occurred and AutoVP has been halted. The error was:\r\n" + err)
)
)

-- macroscript definition
macroscript AutoVP_mcr category:"SplutterFish" buttontext:"AutoVP" tooltip:"AutoVP - MouseOver Viewport Switcher (v0.04)" (
on execute do (
if (((maxVersion())[1] < 9000) OR (not autoVP_useDotNetTimer)) then (
if (autoVP_roll.ui_isOpen) then (
autoVP.closeUI()
)
else (
autoVP.showUI()
)
)
else (
if (autoVP.dotNetTimer == undefined) then (
autoVP.dotNetTimer = dotNetObject "System.Windows.Forms.Timer"
dotnet.addEventHandler autoVP.dotNetTimer "tick" autoVP_dotNyetInvoke
)
if (autoVP.dotNetTimer.enabled) then ( autoVP.dotNetTimer.stop() )
else (
autoVP.dotNetTimer.interval = autoVP_interval
autoVP.dotNetTimer.start()
)
updateToolbarButtons()
)
)
on isChecked do (
if (autoVP != undefined) then (
if (((maxVersion())[1] < 9000) OR (not autoVP_useDotNetTimer)) then (
autoVP_roll.ui_isOpen AND autoVP_roll.ticktock.active
)
else (
if (autoVP.dotNetTimer != undefined) then ( autoVP.dotNetTimer.enabled )
else ( false )
)
)
else ( false )
)
on isEnabled do ( autoVP != undefined )
)
updateToolbarButtons()

cyfer
10-10-2008, 09:56 PM
wow ZeBoxx2 , this is really amazing

but the 2nd version is too Dangerous
if anything depending on a dotNetTimer fails , you have a complete disaster ! , at least that what i knew from experimenting with the dotNetTimer

as for the 2nd version , i'm having this habit of working like in maya , you know ..maximize is a spacebar shortcut , when i do that with the 2nd version and hover over the front viewport and hit the spacebar i get ....Zillion errors ! , i don't know in general how to stop that dotNetTimer in such case and after Esc .........i end up closing with Ctrl+Alt+Delete !

runtimeError: viewprot.activeviewport index out of range
http://img337.imageshack.us/my.php?image=vpautott4.jpg
stuff like that
i installed it in the startup folder , assigned a shortcut to the macro ....
am i doing something wrong or what ?

ZeBoxx2
10-10-2008, 10:51 PM
yeah, that's what I was afraid of with regard to the dotNetTimer, as I mentioned in my earlier posts... it's hailed as some kind of holy grail of not having to have an UI in view.. but at the same time, getting the darn thing to die DIE DIE is.. well. And of course because it happily keeps running despite an error, you don't just get 1 error popup.. you get numerous ;)

So.... 1. I'll check what happens with that spacebar thing and 2. I'll make the timer method in 3ds Max 9+ a preference option ;)

ZeBoxx2
10-10-2008, 11:12 PM
there you go.. script updated (same post).. The maximize view (a layout change) bug should be fixed, a startup bug should be fixed (whoops) and the .NET Timer use is now a preference; just set the appropriate variable to false and you should be all set.
( I could 'hide' the UI by just opening it up off-screen, I suppose, for those who dislike the dialog but don't want to use the .NET Timer either... thoughts? %) )

cyfer
10-11-2008, 03:16 AM
works like a charm .

as for the killing issue , i can't think of anything but another macro to kill it
while a screen draw that you click to end the macro would be elegant , it raises the question in what viewport should it be ?
the active one ? wouldn't that be slow a little ? ...

ZeBoxx2
10-11-2008, 02:47 PM
another macro won't do much when you're stuck in one of the error dialogs and, as soon as you click OK, another one pops up ;)
I just put the function that's indirectly called in a try/catch and halt the .NET Timer if an error is throw (code adjusted up above).


Now if only I could figure out how to detect if max is the active window.. better yet, the main UI, to stop the (possibly distracting) viewport changes when you're in another dialog/application.
There's the mainWindowEnabled callback, but that only triggers when a modal dialog pops up / closes. Boo.

ZeBoxx2
03-06-2009, 09:52 AM
minor update - v1.01 - and a proper listing now;
http://www.scriptspot.com/3ds-max/autovp-mouseover-viewport-switcher

change for v1.01 is some internal logic changes, exposing the scripted bits outside of the macroscript, and letting you pick between the .NET vs dialog method by holding the shift key when opening.

Insanto
03-06-2009, 07:05 PM
i'm not rlly experienced with callbacks so my idea might be complete bull but wouldn't it be possible to make a callback wired to the mouse.Pos ..when it changes?
just a thought

didn't test your script yet but it sounds like a mustHave :thumbsup:

ZeBoxx2
03-07-2009, 01:55 AM
'fraid there's no mouse position change callback in 3ds Max itself - there probably is in .NET but then you'd have to filter the *many* mouse position changes to a more reasonable level for the code to be invoked :) Might as well use the timer ;)

Looks like scriptspot is still waiting to process the uploads, so I'll attach that new version here as well -_-

AdrianWilliams
10-29-2009, 12:18 PM
AWESOME ZeBoxx2, such a handy tool, makes things much more easier!
Thanks
-A

CGTalk Moderation
10-29-2009, 12:18 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.