PDA

View Full Version : dotNet timer - struct issues


SyncViewS
10-02-2009, 12:20 PM
Hi guys,
I'm putting together a quite simple timer struct with dotNet, to avoid the interface timer. I run into some problems though. Following code shows the timer struct instanced and called from within another struct. It fires the error:

MAXScript dotNet event handler Exception
-- Runtime error: Struct member access requires instance: theTimer

I'm a completely newbie when it comes to dotNet. What does the error mean? What I'm trying to do, is to call a function once, after a time interval.

Thank you very much.

struct DotNetTimer
(
theTimerObject = (dotNetObject "System.Windows.Forms.Timer"),

function setOnTick onTick =
(
if (isKindOf onTick MAXScriptFunction) do
(
dotNet.removeAllEventHandlers theTimerObject
dotNet.addEventHandler theTimerObject "tick" onTick
)
),

function setInterval iInterval =
(
theTimerObject.interval = iInterval
),

function start =
(
theTimerObject.start()
),

function stop =
(
theTimerObject.stop()
),

function restart =
(
theTimerObject.stop()
theTimerObject.start()
)
)


struct DummyStruct
(
theTimer = DotNetTimer(),

function getTime =
(
print localTime
theTimer.stop()
),

function init =
(
theTimer.setInterval 5000
theTimer.setOnTick getTime
),

function runIt =
(
theTimer.start()
)
)


d = DummyStruct()
d.init()

--WARNING uncomment this to give the code a try, but it hangs 3ds Max
--d.runIt()
- Enrico

Bobo
10-02-2009, 12:46 PM
Enrico,

There are two types of struct use in MAXScript.
If a struct contains ONLY functions, you can call these functions directly from the struct definition.

For example,

struct someStruct
(
fn funct1 = (10*20),
fn funct2 = (30*someStruct.funct1())
)

--you may say:

someStruct.funct2()


#Struct:someStruct(
funct2:<fn>; Public,
funct1:<fn>; Public)
6000


NOTE that you MUST provide the struct name as prefix of the funct1() call so it knows where to look. Otherwise, you will get the error

-- Runtime error: Struct member access requires instance: funct1

But if your struct contains both properties and functions, you MUST first create an instance of the struct before accessing its properties (you can still call functions from the def or an instance)

struct someStructDef
(
v1 = 42,
fn funct1 = (v1),
fn funct2 = (funct1()*2)
)

theStruct = someStructDef()
theStruct.v1
theStruct.funct2()

#Struct:someStructDef(
funct2:<fn>; Public,
v1:<data>; Public,
funct1:<fn>; Public)
(someStructDef v1:42)
42
84


NOTE that once you create an instance of the struct, you can access its members (properties and functions) from inside struct functions without prefixing the function name with the name of the struct.

SyncViewS
10-02-2009, 01:21 PM
Thanks Bobo for the very quick reply.

There's still something I don't get. I'm aware of the struct instancing, but I thought I made an instance of struct DotNetTimer, when created an instance of DummyStruct. DummyStruct is instanced as d = DummyStruct(). Don't apply object composition rules? (C++)

What am I missing? Isn't first definition in struct DummyStruct: theTimer = DotNetTimer() an instance of struct DotNetTimer?

How should I call DotNetTimer methods from within DummyStruct methods? d.init() works well, but when the timer ticks the call to DummyStruct.getTime() fails. I tried to fully specify the path: theTimer.setOnTick DummyStruct.getTime, but in that case it was simply ignored.

From another point of view, does the issue arise from DotNetTimer not knowing what's the instance of DummyStruct, consequently not being able to call getTime method? How would I fix this?

Thank you very much

- Enrico

Bobo
10-02-2009, 01:39 PM
I will check this out when I get to the office... Stay tuned.

SyncViewS
10-02-2009, 01:49 PM
I'm sorry Bobo, it still doesn't work. I tried to create the two instances, then assigned the DotNetTimer instance to a property of the instance of DummyStruct, but it keeps giving the same error. I'm lost.

struct DotNetTimer
(
theTimerObject = (dotNetObject "System.Windows.Forms.Timer"),

function setOnTick onTick =
(
if (isKindOf onTick MAXScriptFunction) do
(
dotNet.removeAllEventHandlers theTimerObject
dotNet.addEventHandler theTimerObject "tick" onTick
)
),

function setInterval iInterval =
(
theTimerObject.interval = iInterval
),

function start =
(
theTimerObject.start()
),

function stop =
(
theTimerObject.stop()
),

function restart =
(
theTimerObject.stop()
theTimerObject.start()
)
)

struct DummyStruct
(
theTimer = undefined,

function getTime =
(
print localTime
theTimer.stop()
),

function init =
(
theTimer.setInterval 5000
theTimer.setOnTick getTime
),

function runIt =
(
theTimer.restart()
)
)

theTimerInstance = DotNetTimer()

theDummy = DummyStruct()

theDummy.theTimer = theTimerInstance
theDummy.init()

--WARNING uncomment this to give the code a try, but it hangs 3ds Max
--theDummy.runIt()
- Enrico

EDIT: Sorry I didn't see your edit. Thank you very much for your effort.

Bobo
10-02-2009, 03:10 PM
I think I got it to work now.

Basically, you have a scope visibility issue:

theTimer.setOnTick getTime

passes the function getTime hosted by the instance of DummyStruct as the event handler of the timer. So when the timer starts ticking, it tries to call that function.
The function itself looks like

function getTime =
(
print localTime
theTimer.stop()
),

where theTimer is in the local scope of DummyStruct's instance, but the handler lives in the OTHER struct and cannot see that, so right after printing the time, it complains it cannot access theTimer.

I declared the variable d as global before the two structs definitions and inside the getTime function, I had to prefix theTimer with d. because it is sent to work in a different scope when passed to the event handler.

This is of course not a very clean solution, but at least it does not fail.

SyncViewS
10-02-2009, 04:49 PM
Thank you Bobo, now I see the scope issue. I cannot really understand why it doesn't work by fully specifying the path to DummyStruct.theTimer.stop(), as "DummyStruct" should be replaced by the struct instance, and so be accessible from outer scope.
Anyway, I found an alternative way to solve path issues avoiding global variables, even if it isn't so clean either. Thanks again for your effort and tackling my problem.

(

struct DotNetTimer
(
theTimerObject = (dotNetObject "System.Windows.Forms.Timer"),

function setInterval iInterval =
(
theTimerObject.interval = iInterval
),

function start =
(
theTimerObject.start()
),

function stop =
(
theTimerObject.stop()
),

function restart =
(
theTimerObject.stop()
theTimerObject.start()
),

function setOnTick onTick =
(
if (isKindOf onTick MAXScriptFunction) do
(
format "inFunction: %\n" onTick
format "here!\n"

dotNet.removeAllEventHandlers theTimerObject
dotNet.addEventHandler theTimerObject "tick" onTick

format "here too!\n"
)
)
)

struct DummyStruct
(
theDummyTimer = undefined,

function getTime =
(
print localTime
::theTimer.stop()
),

function init &inTimer =
(
theDummyTimer = inTimer
theDummyTimer.setInterval 5000
theDummyTimer.setOnTick getTime
),

function runIt =
(
theDummyTimer.restart()
)
)

theTimer = DotNetTimer()
theDummy = DummyStruct()

theDummy.init &theTimer
theDummy.runIt()

)
- Enrico

CGTalk Moderation
10-02-2009, 04:49 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.