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

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.

Related

What is the right way to close a frame in wxWidgets

I have created an application that has a ShellFromFBFrame inheriting from wxFrame window.
The App object is defined as follows:
bool ShellFromFBApp::OnInit()
{
//(*AppInitialize
bool wxsOK = true;
wxInitAllImageHandlers();
if ( wxsOK )
{
ShellFromFBFrame* Frame = new ShellFromFBFrame(0);
Frame->Show();
}
//*)
return wxsOK;
}
The ShellFromFBFrame is as follows:
ShellFromFBFrame::ShellFromFBFrame(wxWindow* parent,wxWindowID id)
{
//(*Initialize(ShellFromFBFrame)
wxBoxSizer* MainSizer;
wxBoxSizer* mainContentSizer;
wxMenu* createContact;
...
the ShellFromFBFrame opens a new CreateContactFrame:
void ShellFromFBFrame::OnCreateContact(wxCommandEvent& event)
{
CreateContactFrame* createContactFrame = new CreateContactFrame(NULL,wxID_ANY);
createContactFrame->Show(true);
}
The CreateContactFrame is as follows:
CreateContactFrame::CreateContactFrame(wxWindow* parent,wxWindowID id)
{
//ctor
Create(parent, id, wxT("Create Contact"), wxDefaultPosition, wxSize(1100,700), wxDEFAULT_FRAME_STYLE, _T("id"));
// int num;
// num = wxAtoi(row);
//this->rowToFetch = row;
...
BEGIN_EVENT_TABLE(CreateContactFrame, wxFrame)
EVT_BUTTON(ID_CREATEBTN, CreateContactFrame::CreateContact)
END_EVENT_TABLE()
But when I close the CreateContactFrame window by close button or cancel button. My App crashes and I get the following process terminated error message in build logs:
Process terminated with status -1073740940 (0 minute(s), 12 second(s))
What am I doing wrong?
You're running into a stack overflow (how thematic!) due to an infinite recursion: your wxEVT_CLOSE handler calls Close() resulting in another wxEVT_CLOSE being generated and so on. Simply remove the handler completely if you have nothing to do in it to fix the problem.
Also, when encountering a reproducible crash, the first thing to do is to build your program with debug information, run it under debugger and look at the stack at the moment of the crash -- in 90% cases this will provide you with the answer, and this would definitely have been the case here when you would have seen the endlessly repeating calls to your OnClose() in the stack.
It was not a coding issue but rather a build of wxwidgets library. Works fine with MSVC 2019.

How to obtain the set of all signals for a given widget?

I am looking through the Qt documentation. Is there a quick and dirty way to get a list of all signals that a widget can emit.
For example (withPyQt):
allSignalsList = thisWidget.getSignals()
Alternatively, is there is a nice place on the new Qt5 API that shows all the signals for a given QObject?
There's no built-in method for listing signals, but normal python object introspection will get the information fairly easily:
from PyQt5 import QtCore, QtWidgets
def get_signals(source):
cls = source if isinstance(source, type) else type(source)
signal = type(QtCore.pyqtSignal())
for subcls in cls.mro():
clsname = f'{subcls.__module__}.{subcls.__name__}'
for key, value in sorted(vars(subcls).items()):
if isinstance(value, signal):
print(f'{key} [{clsname}]')
get_signals(QtWidgets.QPushButton)
Output:
clicked [PyQt5.QtWidgets.QAbstractButton]
pressed [PyQt5.QtWidgets.QAbstractButton]
released [PyQt5.QtWidgets.QAbstractButton]
toggled [PyQt5.QtWidgets.QAbstractButton]
customContextMenuRequested [PyQt5.QtWidgets.QWidget]
windowIconChanged [PyQt5.QtWidgets.QWidget]
windowIconTextChanged [PyQt5.QtWidgets.QWidget]
windowTitleChanged [PyQt5.QtWidgets.QWidget]
destroyed [PyQt5.QtCore.QObject]
objectNameChanged [PyQt5.QtCore.QObject]
However, it's probably better to learn to use the Qt Documentation. If you go to the page for a Qt class, there's a Contents sidebar on the top-right which has links for the main member types. This usually includes a section for signals, but if it doesn't, you can drill down through the inherited classes until you find one.
So for example, the QPushButton page doesn't show a signals section, but it inherits QAbstractButton, which does have one.
The trick is to use the QObject's meta object to iterate through the QObject's methods, then pick out the ones that have a signal type.
For example, this code snippet will print out the names of QThread's signals:
QThread thread;
QMetaObject metaObject = thread.staticMetaObject;
for (int i = 0; i < metaObject.methodCount(); i++) {
QMetaMethod method = metaObject.method(i);
if (method.methodType() == QMetaMethod::Signal) {
qDebug() << "Signal: " << method.name();
}
}
It should be trivial to adapt that to put the QMetaMethods into a QList or any other data structure.

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()
}

qt5 proxy model updating too soon before main model update is done

I have a setup with a main model (QStandardModel), a proxy model which changes the output of the DisplayRole, and a separate tableview displaying each model. Inside the main model data is a user role that stores a pointer to another QObject which is used by the proxy model to get the desired display value.
I'm running into problems when the object pointed to by that variable is deleted. I am handling deletion in the main model via the destroyed(QObject*) signal. Inside the slot, I search through the model looking for any items that are pointing to the object and delete the reference.
That part works fine on its own but I also have connected to the onDataChanged(...) signal of the proxy model, where I call resizeColumnsToContents() on the proxy model. This then calls the proxy's data() function. Here I check to see if the item has a pointer and, if it does, get some information from the object for display.
The result of all this becomes:
Object about to be deleted triggers destroyed(...) signal
Main model looks for any items using the deleted object and calls setData to remove the reference
Tableview catches onDataChanged signal for the proxy model and resizes columns
Proxy model's data(...) is called. It checks if the item in the main model has the object pointer and, if so, displays a value from the object. If not, it displays something else.
The problem is, at step 4 the item from the main model apparently still hasn't been deleted; the pointer address is still stored. The object the pointer was referencing, though, has been deleted by this point resulting in a segfault.
How can I fix my setup to make sure the main model is finished deleting pointer references before the proxy model tries to update?
Also, here is pseudo-code for the relevant sections:
// elsewhere
Object *someObject = new QObject();
QModelIndex index = mainModel->index(0,0);
mainModel->setData(index, someObject, ObjectPtrRole);
// do stuff
delete someObject; // Qt is actually doing this, I'm not doing it explicitly
// MainModel
void MainModel::onObjectDestroyed(QObject *obj)
{
// iterating over all model items
// if item has pointer to obj
item->setData(QVariant::fromValue(NULL), ObjectPtrRole));
}
// receives onDataChanged signal
void onProxyModelDataChanged(...)
{
ui->tblProxyView->reseizeColumnsToContents();
}
void ProxyModel::data(const QModelIndex &index, int role) const
{
QModelIndex srcIndex = mapToSource(index);
if(role == Qt::DisplayRole)
{
QVariant v = sourceModel()->data(srcIndex, ObjectPtrRole);
Object *ptr = qvariant_cast<Object*>(v);
if(ptr != NULL)
return ptr->getDisplayData();
else
return sourceModel->data(srcIndex, role);
}
}
The problem is ptr is not NULL, but the referenced object is deleted, at the time ProxyModel::data(...) is called so I end up with a segfault.
To avoid dangling pointer dereferences with instances of QObject, you can do one of two things:
Use object->deleteLater - the object will be deleted once the control returns to the event loop. Such functionality is also known as autorelease pools.
Use a QPointer. It will set itself to null upon deletion of the object, so you can check it before use.

How to pass data to google-maps callback functions and prevent memory leaks

I am working with a vast of markers 10.000+ and all of them should be clickable. At the moment I create a separate callback function for each marker, which is a closure over all the data I need. I have tried to prevent the creation of 10.000 functions, which do all the same and pass the data I need as an argument to the cb-function, but this seems impossible.
But there is a way to get the data to the callback function anyway. 'This' points, int the cb-function, to the actual clicked marker and I can access the required data, if I attach them to the marker object. This is very dirty but it seems to work.
But this causes further problems: I doubt this causes memory leaks. I reference the marker in the Object which the marker stands for like obj.marker but to access the object in the cb-function I have to reference the object like marker.obj
// Callback function for clicks on markers
function cb(e){
console.log(e); // the event with information about marker position
console.log(this.obj); // the object the marker stands for
}
//add a eventListener to the marker which represents a object
this.initMarkerListener = function(obj) {
obj.marker.obj = obj; //the critical point
google.maps.event.addListener(obj.marker, 'click', cb);
};
Does this creates a memory leak? And can I pass the required data with a other technique to the callback function?
There's nothing wrong with the approach you've taken. A memory leak occurs when references that you expect to 'clean up' (let the GB clean up unused references) are left behind.
Consider then, that if marker exists in memory, and you delete obj, because marker holds a reference to obj, then the GB will not pick obj up to free up that memory part.