How to close a UIFrame window from the object itself? (Behavioral difference GMS 1.x to GMS 2.x) - dm-script

In GMS2.x, closing UIFrame window with the code shown below will cause DM to crash (at pressing the close button.)
However, the same code works fine with GMS 1.x.
Is there a way to work around this problem in GMS 2.x?
class UIWindowCloseTest : UIFrame {
void CloseSelf( object self ) self.GetFrameWindow().WindowClose(0);
UIWindowCloseTest( object self ) {
TagGroup tgDialog = DLGCreateDialog( "window close test" );
tgDialog.DLGAddElement( DLGCreatePushButton( "Close", "CloseSelf" ));
self.super.init(tgDialog);
self.Display( "test" );
result( self.ScriptObjectGetID().Hex() + " constructed\n" );
};
~UIWindowCloseTest( object self ) \
result( self.ScriptObjectGetID().Hex() + " destructed\n\n" );
};
alloc(UIWindowCloseTest);

Yes,
in GMS 2.x you have to use
self.close();
instead of
self.GetFrameWindow().WindowClose(0);

This is the extension of this questions for GMS 3.X:
In essense, the answer below is correct for GMS 3 as well, but only since its version 3.2 (Maybe GMS 3.1.2 as well).
Earlier versions of GMS 3 have a bug as KEVIVI pointed out in the comment to the answer.
However, there is a work-around solution to this, which is slightly elaborate:
Class myDLG : UIframe
{
myDLG(object self) result("\n Create DLG")
~myDLG(object self) result("\n Kill DLG")
void DeferredClose( object self )
{
TagGroup tgs = GetPersistentTagGroup()
number scriptID
if ( tgs.TagGroupGetTagAsLong( "DummyTag_CloseWindow_ID", scriptID ) )
{
object obj = GetScriptObjectFromID( scriptID )
if ( obj.ScriptObjectIsValid() )
{
obj.GetFrameWindow().WindowClose(0)
return
}
}
Debug( "\n Sorry, but could not close dialog." )
}
void CloseButtonAction( object self )
{
// Normally, it would be save to use "self.close()" here,
// but due to a bug, this is currenlty not possible in GMS 3.1
// The alternative method of getting the window of the UIframe object
// and closing it, is okay, but it must not be called directly here,
// or it will crash DM.
// As a work-around, one can store the object ID and have a separate
// thread pick it up, get the object, and close the object's window.
// This is, what we are doing below.
// Write ScriptID into tags
TagGroup tgs = GetPersistentTagGroup()
tgs.TagGroupSetTagAsLong( "DummyTag_CloseWindow_ID", self.ScriptObjectGetID() )
// Launch separate thread just to close... (0.1 sec delay for safety)
AddMainThreadSingleTask( self, "DeferredClose", 0.1 )
}
TagGroup CreateDLG(object self)
{
TagGroup DLGtg,DLGtgItems
DLGtg=DLGCreateDialog("my Dialog",DLGtgItems)
DLGtgItems.DLGAddElement(DLGCreatePushButton("Close","CloseButtonAction"))
return DLGtg
}
}
{
object dialog=Alloc(myDLG)
dialog.Init( dialog.CreateDLG() )
dialog.display("")
}
So:
For GMS 3.2 and later: Use self.close();
For GMS 3.0 and 3.1 (with the bug): Use the workaround.
For GMS 2.x: Use self.close();
For GMS 1.x: Use self.GetFrameWindow().WindowClose(0);

Related

updating label with progress of http post not working. IllegalStateException

I am having trouble with binding a UI component to an observable that gets updated progress from a http post event. I get an IllegalStateException
As I understand it the issue is the bind update is not happening on the UI thread. The answers I have read say that I need to use runAsync and then specify a UI block to update the UI component, but I am at a loss for how to accomplish this.
// View class
private val controller: ZumController by inject()
item("_Upload") {
isMnemonicParsing = true
action {
controller.uploadToServer()
}
}
bottom = label() {
useMaxWidth = true
padding = Insets(5.0, 10.0, 5.0, 10.0)
this.bind(controller.progress)
}
// Controller class
var progress = SimpleStringProperty("Select images to upload")
fun uploadToServer() {
images.forEach{ p ->
Fuel.upload("http://127.0.0.1:8089")
.add {FileDataPart(File(p), name = "file")}
.progress { readBytes, totalBytes ->
progress.value = (readBytes.toFloat() / totalBytes.toFloat() * 100).toString()}
.response { _ -> }
}
}
How would I go about making sure the UI is updated during the application thread when I need progress before function call (uploadToServer()) returns? Sorry if this has already been answered, I still don't get exactly what is happening here.
I've solved my problem with the following changes. I pass the FXTask to function uploadToServer(). There I updateMessage() with the progress callback for the http POST request. I can't say its the best way but it works. feel free to update this answer with more clear and concise code
item("_Upload") {
isMnemonicParsing = true
action {
runAsync {
controller.uploadToServer(this)
} ui {
}
}
}
fun uploadToServer(task: FXTask<*>) {
images.forEach{ p ->
Fuel.upload("http://127.0.0.1:8089")
.add {FileDataPart(File(p), name = "file")}
.progress { readBytes, totalBytes ->
val perComplete = readBytes.toFloat() / totalBytes.toFloat() * 100
task.updateMessage("Uploading $p %.2f".format(perComplete).plus("%"))
}
.response { _ -> }
}
}
TornadoFX has a built in TaskStatus object which has properties for the progress of the task. You can bind one or more of the properties in the TaskStatus object to any UI element, and simply call updateProgress from within your controller function. You don't even need to pass in the TaskStatus object, as the default instance will be used if you don't.
There are a few test appa within the framework that does this:
https://github.com/edvin/tornadofx/blob/master/src/test/kotlin/tornadofx/testapps/AsyncProgressApp.kt
https://github.com/edvin/tornadofx/blob/master/src/test/kotlin/tornadofx/testapps/TaskStatusTest.kt
That said, a quick and dirty solution for updating the UI from any other thread is simply wrapping the UI manipulation code inside runLater {}. This will work equally well for just updating a label for example.

applyEdits fails in ArcGIS JSAPI

I am using a feature layer in JSAPI, where I edit a feature by the standard selection, change attributes, applyEdits process. It seems that there is a bug in JSAPI both 3.12 and 3.14 which makes the apply edits fail on certain features. The callback just errors out without any clue.
The interesting observations:
Failure only happens on certain features and all cases belong to a particular layer (other features in that layer are just fine).
Changing from 3.12 to 3.14 the features that cause the error are changed but did not go away.
Here is a quick snippet of the code:
sq = buildSelectionQuery();
if (sq) {
all(assetsFeatureLayers.map(function (l) { return l.selectFeatures(sq, esri.layers.FeatureLayer.SELECTION_NEW).promise;})
).then(function (fls) {
console.log('sel res', fls);
all(fls.map(function (r, i) {
var fs = r[0]; // the first is an array of selected features
var l = assetsFeatureLayers[i];
console.log(fs);
if (fs.length > 0) {
console.log("Switching phases to: ", tph);
fs.forEach(function (f) {
f.attributes['phasecode'] = tph;
});
console.log("Saving switched phases for layer: ", l.name);
return l.applyEdits(null, fs, null); //.promise;
} else {
return null;
}
})).then(function (l) {
console.log("Phase switching finished successfully!", l);
clearAllSelections();
}, function (e) {
console.log("Error switching phases!", e);
clearAllSelections();
});
});
}
OK, I found the root cause. We are using ESRI's PHP proxy, and it was a bug in that. Upgrading to the latest 1.1 beta solved the issue. Here is the link to their repo: https://github.com/Esri/resource-proxy

How to let a DigitalMicrograph script object be notified when an image is closed

We can attach DM script objects to image events and imageDisplay events via the ImageAddEventListener() and ImageDisplayAddEventListener(). Are there event map message to support the "image close" event? I tried both the "image_about_to_close" and the "imagedisplay_about_to_close" and they don't seem to work.
DigitalMicrograph differentiates between Images (The data object), ImageDisplays (an object rending the data), ImageDocuments (The object which is represented by the saved file), and DocumentWindows (the window object in which an ImageDisplay is shown).
The event you are asking for belongs to DocumentWindows and not to Images nor to ImageDisplays. Accordingly, you need to register it with a DocumentWindow object. This is done with the command WindowAddWindowListener.
BTW, there are two similar events you can capture, one is window_closed and the other is window_about_to_close. The later is fired before the user is prompted to save unsaved data, the other one after such a prompt but - inconsistently - before the window is actuallay removed from memory. (There is still a pointer to it at that time.)
The following script attaches the two events. I've put it into a background-thread for easier testing.
// $BACKGROUND$
Class MyWindowListenerClass
{
MyWindowListenerClass( object self ) Result("\n object 0x"+self.ScriptObjectGetID().Hex()+" created.")
~MyWindowListenerClass( object self ) Result("\n object 0x"+self.ScriptObjectGetID().Hex()+" destroyed.")
Void HandleAboutToClosedAction( object self, number e_fl, DocumentWindow Win )
{
Result(GetTime(1)+": Window about-to-closed message : 0x" + Hex(e_fl,8) + "\n" )
}
Void HandleClosedAction(object self, number e_fl, DocumentWindow Win)
{
Result(GetTime(1)+": Window closed message : 0x" + Hex(e_fl,8) + "\n" )
}
}
Void main()
{
Object objListener
Image img
DocumentWindow win
String messagemap
Number ListenerID
img:=RealImage("Test",4,100,100)
img.ShowImage()
win = img.imageGetOrCreateImageDocument().ImageDocumentGetWindow()
messagemap += "window_closed:HandleClosedAction;"
messagemap += "window_about_to_close:HandleAboutToClosedAction;"
objListener = Alloc(MyWindowListenerClass)
ListenerID = win.WindowAddWindowListener( objListener, messagemap)
While(!ShiftDown()) 1==2
win.WindowRemoveWindowListener(ListenerID)
}
main()
A final note: Having an DocumentWindow closed does not necessarily mean you have released the image as well. As long as some script or other code keeps a reference to the Image it will stay in memory! Be sure to not do this, or you might see memory leaks in your scripts. It is generally saver to 'store' ImageIDs as member variables than image-objects in such a case, as they don't "lock" the Image.

How can one set up persistent collaborating objects in DigitalMicrograph via scripting?

I have come to really appreciate the benefits of using objects to deploy a given application within the DigitalMicrograph environment via the DMS language. The object-oriented approach opens the door to the use of reusable design patterns involving collaborating objects, e.g. Model-View-Controller (MVC). However, objects within DM seem to be highly volatile due to the use of automatic reference counting to manage their life cycles. In order for an MVC trio, or any other set of collaborating objects, to stay alive long enough to be useful, at least one of them must be rooted in a non-volatile object managed by the DM application. So far, the only such objects I have come across within DM are those based on the UIFrame class (i.e. modeless dialogs and UI palettes). For MVC implementations, this works out fine since it makes sense to implement the View as a UIFrame object. It's just a bit unconventional in that the View object becomes the root object that keeps the MVC trio alive and functioning. Normally it is the Controller object that is rooted in the application and manages the Model and View objects. But what about design patterns that do not involve UI? Is there any (acceptable) way to give a set of collaborating objects persistence without rooting them in a UIFrame object? Are there other application-rooted object types that can serve this purpose? I assume setting up a reference cycle would not be an acceptable approach due to the inevitable risk of memory leaks.
The third, and by far the best and cleanest solution is to launch your object as a 'listener' to some event. As you are looking for an object which should stay in scope as long as DigitalMicrograph is open, its possibly best to listen to the application itself. By listening for the "about_to_close" message you also get the ideal handle to properly release all resources before shutdown. The code is the following:
From my 3 answers this is the one I would use. (The others should just illustrate options.)
class MyPermanentObject
{
MyPermanentObject( object self ) { result("created MyPermanentObject :"+self.ScriptObjectGetID()+"\n");}
~MyPermanentObject( object self ) { result("killed MyPermanentObject :"+self.ScriptObjectGetID()+"\n");}
void DeInitialize( object self, number eventFlags, object appObj )
{
OKDialog( "The application is closing now. Deinitialize stuff properly!" );
}
}
{
object listener = Alloc( MyPermanentObject )
ApplicationAddEventListener( listener, "application_about_to_close:DeInitialize" )
}
I can think of various ways to get this persistence, but the one which jumped to mind first was to launch one object into a background thread, like in the example below. The actual background thread can check every so often if the object should still remain, and by sharing the object ID with the outside world, other objects (which don't have to be persistent) can access the "anchored" object.
A word of warning though: If you keep things in memory like this, you have to be careful when closing DigitalMicrograph. If the object hangs on to some items DM wants to destroy, you might see errors or crashes at the end.
// This is the object "anchored". It will remain in memory, because we launch it on a separate thread.
// On this thread, it loops until a variable is set to false (or until SHIFT is pressed)
Class IPersist : Thread
{
number keepme
IPersist( object self ) { result("created IPersist:"+self.ScriptObjectGetID()+"\n");}
~IPersist( object self ) { result("killed IPersist:"+self.ScriptObjectGetID()+"\n\n\n\n");}
void CallFromOutside( object self ) { Result( "\t IPersist can be used!\n" ); }
void StopFromOutside( object self ) { keepme = 0; }
void RunThread( object self )
{
keepme = 1
Result( "\t Called once at start.\n")
While( keepme && !ShiftDown() ) yield()
Result( "\t Finished.\n")
}
}
// Just and example class used to access the 'anchored' object
Class SomethingElse
{
number keepID
SomethingElse( object self ) { result("created SomethingElse:"+self.ScriptObjectGetID()+"\n");}
~SomethingElse( object self ) { result("killed SomethingElse:"+self.ScriptObjectGetID()+"\n");}
void SetKeepID( object self, number id ) { keepID = id; }
void CallOut( object self )
{
result( "SomethingElse object is accessing CallOut...\n" )
object p = GetScriptObjectFromID( keepID )
if ( p.ScriptObjectIsValid() )
{
p.CallFromOutside()
}
}
void CallStop( object self )
{
result( "SomethingElse object is accessing CallOut...\n" )
object p = GetScriptObjectFromID( keepID )
if ( p.ScriptObjectIsValid() )
{
p.StopFromOutside()
}
}
}
// Main script. Create object on separate thread. Then feed it's ID as "weak reference" into the second object.
{
object ob = Alloc(IPersist)
ob.StartThread()
object other = Alloc(SomethingElse)
other.SetKeepID( ob.ScriptObjectGetID() )
other.CallOut()
If ( TwoButtonDialog( "You can either stop IPerstis now, or by pressing SHIFT later.", "Stop now", "later" ) )
other.CallStop()
}
An alternative way would be to have two objects keep references of each other. This is a deadlock-situation one would normally rather avoid, but for the purpose of anchoring it works as well. No object can go out of scope until you release one on purpose.
Again, it is your responsibility to 'release' things when you want a proper shutdown of the system.
The code for the deadlock-situation is rather slim:
class SelfLock
{
object partner
SelfLock( object self ) { result("created SelfLock:"+self.ScriptObjectGetID()+"\n");}
~SelfLock( object self ) { result("killed SelfLock:"+self.ScriptObjectGetID()+"\n");}
void SetPartner(object self, object p) { partner = p; }
void ReleasePartner(object self) { partner = NULL; }
}
{
object p1 = Alloc(SelfLock)
object p2 = Alloc(SelfLock)
p1.SetPartner(p2)
p2.SetPartner(p1)
if ( TwoButtonDialog( "Release partner", "Yes", "No keep locked" ) )
p1.ReleasePartner()
}

hand tracking not working after a reload of openni dynamic library

Our project is (http://www.play4health.com/p4h_eng/) using Ogre 3D
over Ubuntu 11.04. Except for core services all is based in a plugin architecture taking advantage of Ogre 3d plugin facilities.
In our plugin architecture plugins can be:
Videogames
Interaction methods
Users configure their session creating tuples (videogame, interaction
method). The flow is a session is:
* User load his session.
* User click of one of the tuples for the session and play to
videogame with a specific interaction method.
* Repeat it until end all activities of the session.
Plugin are loaded/unloaded dynamically by demand.
One of this interaction methods is hand tracking using openni. What is
the problem?
* Fist time that openni plugin is loading all work perfectly.
* Next time that plugin openni has to be loaded system is able to
detect gestures but not do hand tracking. Note that all plugin are
executed in the same process. Right now only solution is to reboot
platform.
This is the code for init and release OpenNI in our plugin
bool IPKinectPlugin::onInitialise()
{
mHandPointer.mId = "KinectHandPointer";
mHandPointer.mHasAbsolute = true;
mHandPointer.mHasRelative = false;
XnStatus nRetVal = XN_STATUS_OK;
nRetVal = gContext.InitFromXmlFile(String(this->getPluginInfo()-
>getResPath() + "SamplesConfig.xml").c_str());
CHECK_RC(nRetVal, bContext, "InitFromXml");
#if SHOW_DEPTH
nRetVal = gContext.FindExistingNode(XN_NODE_TYPE_DEPTH,gDepthGenerator);
bDepthGenerator = (nRetVal != XN_STATUS_OK);
if (bDepthGenerator)
{
nRetVal = gDepthGenerator.Create(gContext);
CHECK_RC(nRetVal, bDepthGenerator, "Find Depth generator");
}
#endif
nRetVal = gContext.FindExistingNode(XN_NODE_TYPE_USER, gUserGenerator);
bUserGenerator = (nRetVal != XN_STATUS_OK);
if (/*bUserGenerator*/false)
{
nRetVal = gUserGenerator.Create(gContext);
CHECK_RC(nRetVal, bUserGenerator, "Find user generator");
}
nRetVal = gContext.FindExistingNode(XN_NODE_TYPE_GESTURE, gGestureGenerator);
bGestureGenerator = (nRetVal != XN_STATUS_OK);
if (bGestureGenerator)
{
nRetVal = gGestureGenerator.Create(gContext);
CHECK_RC(nRetVal, bGestureGenerator, "Find gesture generator");
XnCallbackHandle hGestureCallbacks;
gGestureGenerator.RegisterGestureCallbacks(gestureRecognized, gestureProcess, 0,
hGestureCallbacks);
}
nRetVal = gContext.FindExistingNode(XN_NODE_TYPE_HANDS,gHandsGenerator);
bHandsGenerator = (nRetVal != XN_STATUS_OK);
if (bHandsGenerator)
{
nRetVal = gHandsGenerator.Create(gContext);
CHECK_RC(nRetVal, bHandsGenerator, "Find hands generator");
XnCallbackHandle hHandsCallbacks;
gHandsGenerator.RegisterHandCallbacks(handsNew, handsMove,handsLost, 0, hHandsCallbacks);
}
nRetVal = gContext.FindExistingNode(XN_NODE_TYPE_DEVICE, gDevice);
bDevice = (nRetVal != XN_STATUS_OK);
gContext.RegisterToErrorStateChange(onErrorStateChanged, NULL, hDummyCallbackHandle);
//Preparo la textura para la webcam
if (bGenerateRGBTexture)
mWebcamTexture = KinectTools::createDepthTexture("KinectWebCamTexture", sPluginName);
return true;
}
//-----------------------------------------------------------------------------
bool IPKinectPlugin::onShutdown()
{
if (bContext)
{
if (bHandsGenerator)
{
gHandsGenerator.StopTrackingAll();
}
if (bGestureGenerator)
{
gGestureGenerator.RemoveGesture(GESTURE_TO_USE);
gGestureGenerator.RemoveGesture(GESTURE_TO_START);
}
gContext.StopGeneratingAll();
gContext.Shutdown();
}
return true;
}
Any idea about this issue? Any wrong with this code?
Maybe you already found a solution in the meantime...
I normally work with the Java Wrapper, but what I see as difference to my code is that I call contect.startGeneratingAll() after creating the generators (Depth, Hands and so on). I had also problems when I did this multiple times at start up. Another difference is that I use a context.release at shutdown.
My procedure is normally:
Init config (License, Nodes, settings)
Create generators
Start Generating All
Run your code ...
Stop Generating ALL
Context release
From OpenNI Documentation
XN_C_API void XN_C_DECL xnShutdown ( XnContext * pContext )
Shuts down an OpenNI context, destroying all its nodes. Do not call
any function of this context or any correlated node after calling this
method. NOTE: this function destroys the context and all the nodes it
holds and so should be used very carefully. Normally you should just
call xnContextRelease()