Как настроить постоянные взаимодействующие объекты в DigitalMicrograph с помощью сценариев?

Я действительно оценил преимущества использования объектов для развертывания данного приложения в среде DigitalMicrograph с помощью языка DMS. Объектно-ориентированный подход открывает двери для использования многократно используемых шаблонов проектирования, включающих взаимодействующие объекты, например. Модель-представление-контроллер (MVC). Однако объекты в DM кажутся очень изменчивыми из-за использования автоматического подсчета ссылок для управления их жизненными циклами. Чтобы трио MVC или любой другой набор взаимодействующих объектов оставался в живых достаточно долго, чтобы быть полезным, по крайней мере один из них должен быть внедрен в энергонезависимый объект, управляемый приложением DM. До сих пор единственные такие объекты, с которыми я сталкивался в DM, основаны на классе UIFrame (т. е. немодальные диалоги и палитры пользовательского интерфейса). Для реализаций MVC это прекрасно работает, поскольку имеет смысл реализовать представление как объект UIFrame. Это немного необычно, поскольку объект View становится корневым объектом, который поддерживает жизнь и функционирование трио MVC. Обычно это объект Controller, который является корневым в приложении и управляет объектами Model и View. Но как насчет шаблонов проектирования, не связанных с пользовательским интерфейсом? Есть ли какой-либо (приемлемый) способ обеспечить постоянство набора взаимодействующих объектов без их укоренения в объекте UIFrame? Существуют ли другие типы объектов с корнем приложения, которые могут служить этой цели? Я предполагаю, что установка эталонного цикла не будет приемлемым подходом из-за неизбежного риска утечек памяти.


person Mike Kundmann    schedule 20.11.2014    source источник


Ответы (3)


Третье и, безусловно, лучшее и чистое решение — запустить ваш объект в качестве «слушателя» какого-либо события. Поскольку вы ищете объект, который должен оставаться в области видимости, пока DigitalMicrograph открыт, возможно, лучше всего слушать само приложение. Прослушивая сообщение «about_to_close», вы также получаете идеальный дескриптор для правильного освобождения всех ресурсов перед завершением работы. Код следующий:

Из моих 3 ответов это тот, который я бы использовал. (Остальные должны просто иллюстрировать варианты.)

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" )
}
person BmyGuest    schedule 20.11.2014
comment
Это звучит как лучшее решение, немного лучше, чем я надеялся. Я не знал о функции ApplicationAddEventListener и о том, что ее можно использовать для эффективной привязки объекта сценария в приложении. Тот факт, что таким образом можно получить уведомление о закрытии приложения, действительно является большим бонусом. Я проверил интерактивную справку для получения дополнительной информации о ApplicationAddEventListener и событиях, которые он распознает, но не нашел подробностей. Есть ли где-нибудь дополнительная документация? - person Mike Kundmann; 21.11.2014
comment
Другими сообщениями, к которым вы можете подключиться таким же образом, являются application_opened, application_closed, image_order_changed. Существует также application_mode_changed, для которого сигнатура метода требует дополнительной строки в конце. - person BmyGuest; 21.11.2014

Я могу придумать разные способы добиться этого постоянства, но первый, который пришел мне в голову, — запустить один объект в фоновом потоке, как в примере ниже. Фактический фоновый поток может время от времени проверять, должен ли объект все еще оставаться, и, делясь идентификатором объекта с внешним миром, другие объекты (которые не должны быть постоянными) могут получить доступ к «привязанному» объекту.

Предупреждение: если вы храните такие вещи в памяти, вы должны быть осторожны при закрытии DigitalMicrograph. Если объект зависает на некоторых элементах, которые DM хочет уничтожить, вы можете увидеть ошибки или сбои в конце.

// 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()
}
person BmyGuest    schedule 20.11.2014
comment
Да, я вижу, что это сработает. Моя главная проблема с этим подходом - это непрерывно работающий цикл while, поскольку объекты потока проверяют условие завершения объекта. И да, тот факт, что было бы трудно предвидеть завершение работы приложения и реагировать на него, является еще одним существенным ограничением. - person Mike Kundmann; 21.11.2014

Альтернативным способом было бы, чтобы два объекта сохраняли ссылки друг на друга. Это тупиковая ситуация, которую обычно лучше избегать, но с целью закрепления она также работает. Ни один объект не может выйти из области видимости, пока вы не отпустите его намеренно. Опять же, вы несете ответственность за «выпуск» вещей, когда вам нужно правильное завершение работы системы.

Код для тупиковой ситуации довольно тонкий:

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()
}
person BmyGuest    schedule 20.11.2014
comment
Это подход, который я изучал до сих пор, и я согласен с тем, что его относительно легко развернуть и использовать. Основная проблема снова в том, что нельзя адекватно реагировать на завершение работы приложения. Я провел несколько тестов и обнаружил, что методы деструктора объектов сценария не вызываются, когда приложение принудительно освобождает объекты сценария при завершении работы. - person Mike Kundmann; 21.11.2014