【问题标题】:How to create a blocking dialog with more than two buttons in dm-script如何在 dm-script 中创建具有两个以上按钮的阻止对话框
【发布时间】:2020-11-24 12:27:43
【问题描述】:

如何创建一个包含两个以上自定义按钮而不是阻止脚本的“确定”和“取消”按钮的对话框?


可以使用TwoButtonDialog() 中创建一个带有两个自定义按钮的对话框。但请考虑以下示例对话框,询问是否覆盖文件。此对话框需要的不仅仅是“确定”和“取消”按钮。

可以使用UIFrame 和对话框函数简单地创建此对话框。但是使用UIFrame::display() 不会等待用户进行交互。将显示对话框并继续执行脚本。当使用UIFrame::pose() 时,这会阻止当前脚本,但也会始终添加“确定”和“取消”按钮。所以在这里使用这些函数似乎不适合这种情况。

如何创建一个对话框来阻止脚本在没有额外的“确定”和“取消”按钮的情况下继续运行,以便我可以添加自己的按钮?并且可选地,我如何使这个对话框成为模态?


我尝试过的

版本 1

我试图通过等待Signal 来阻止当前工作线程,如下面的示例代码所示。我正在创建对话框并使用不添加任何按钮的UIFrame::display()。然后等待按钮点击回调中设置的信号。

number index = -1;
object wait_signal = NewSignal(0);

class ButtonDialog : UIFrame{
    void button_pressed(object self, number i){
        index = i;
        wait_signal.SetSignal();
        self.close();
    }

    object init(object self){
        // creating dialog contents, for full example look at the bottom
        return self;
    }
}    

// Version 1
alloc(ButtonDialog).init().display(title);
wait_signal.waitOnSignal(10, NULL);

result("The index is " + index + "\n");

底部可执行版本,取消注释Version 1块,注释所有其他版本

但这不起作用。出现的对话框未完成,如下所示:

只要我更改窗口,对话框就会显示文本和按钮,但仍然无法点击。加上布局并不完美。

版本 2

所以我想把对话框完全移到一个单独的线程中。这样,它就可以根据需要进行初始化,并且肯定可以使用,因为新的单独线程永远不会被阻塞。

number index = -1;
object wait_signal = NewSignal(0);

class ButtonDialog : UIFrame{
    void button_pressed(object self, number i){
        index = i;
        wait_signal.SetSignal();
        self.close();
    }

    object init(object self){
        // creating dialog contents, for full example look at the bottom
        return self;
    }
}

class ButtonDialogThread : Thread{
    object init(object self){
        return self;
    }
    
    void RunThread(object self){
        alloc(ButtonDialog).init().display(title);
    }
}

// Version 2
alloc(ButtonDialogThread).init().startThread();
wait_signal.waitOnSignal(10, NULL);

result("The index is " + index + "\n");

底部可执行版本,取消注释Version 2块,注释所有其他版本

但这会产生与上面提到和显示的对话框相同的输出。

版本 3

更基本的方法是使用while 循环并在其间休眠。据我所知Signals 的工作方式就是这样。但我还是试了一下。

number index = -1;
object wait_signal = NewSignal(0);

class ButtonDialog : UIFrame{
    void button_pressed(object self, number i){
        index = i;
        wait_signal.SetSignal();
        self.close();
    }

    object init(object self){
        // creating dialog contents, for full example look at the bottom
        return self;
    }
}

// Version 3
alloc(ButtonDialog).init().display(title);
number security_counter = 0;
while(index == -1 && security_counter < 100){
    sleep(0.1);
    security_counter += 1;
}

result("The index is " + index + "\n");

底部可执行版本,取消注释Version 3块,注释所有其他版本

但同样,结果与Version 1 相同。

我做错了什么?如何阻止主线程,直到显示的对话框有一些用户交互?


string text = "Several files already exist. Do you want to overwrite the file 'test_file.dm4' and 12 others?"
string button0 = "Yes";
string button1 = "Yes, all";
string button2 = "No";
string button3 = "No, all";
string button4 = "Cancel";

string title = "Overwrite?";
object wait_signal = NewSignal(0);
number index = -1;

class ButtonDialog : UIFrame{
    void button_pressed(object self, number i){
        index = i;
        wait_signal.SetSignal();
        self.close();
    }
    
    void button0_pressed(object self){self.button_pressed(0);}
    void button1_pressed(object self){self.button_pressed(1);}
    void button2_pressed(object self){self.button_pressed(2);}
    void button3_pressed(object self){self.button_pressed(3);}
    void button4_pressed(object self){self.button_pressed(4);}
    
    object init(object self){
        TagGroup dlg, dlg_items, wrapper, label, b;
        
        index = -1;
        
        dlg = DLGCreateDialog("Press a button", dlg_items);
        
        dlg_items.DLGAddElement(DLGCreateLabel(text));
        
        wrapper = DLGCreateGroup();
        wrapper.DLGTableLayout(5, 1, 1);
        dlg_items.DLGAddElement(wrapper);
        
        b = DLGCreatePushButton(button0, "button0_pressed");
        b.DLGWidth(80);
        wrapper.DLGAddElement(b);
        
        b = DLGCreatePushButton(button1, "button1_pressed");
        b.DLGWidth(80);
        wrapper.DLGAddElement(b);
        
        b = DLGCreatePushButton(button2, "button2_pressed");
        b.DLGWidth(80);
        wrapper.DLGAddElement(b);
        
        b = DLGCreatePushButton(button3, "button3_pressed");
        b.DLGWidth(80);
        wrapper.DLGAddElement(b);
        
        b = DLGCreatePushButton(button4, "button4_pressed");
        b.DLGWidth(80);
        wrapper.DLGAddElement(b);
        
        self.super.init(dlg);
        return self;
    }
}

class ButtonDialogThread : Thread{
    object init(object self){
        return self;
    }
    
    void RunThread(object self){
        alloc(ButtonDialog).init().display(title);
    }
}       

// Version 1
// alloc(ButtonDialog).init().display(title);
// wait_signal.waitOnSignal(10, NULL);

// Version 2
alloc(ButtonDialogThread).init().startThread();
wait_signal.waitOnSignal(10, NULL);

// Version 3
// alloc(ButtonDialog).init().display(title);
// number security_counter = 0;
// while(index == -1 && security_counter < 100){
//  sleep(0.1);
//  security_counter += 1;
// }

result("The index is " + index + "\n");

【问题讨论】:

    标签: dm-script dialog blocking dm-script


    【解决方案1】:

    实际上所有你的尝试都是好的,原则上是有效的。 (第一个是要使用的。)但是,它们不起作用,因为您错过了一点:

    DigitalMicrograph 中的所有 UI 操作都在应用程序的主线程上运行。

    还有:

    默认情况下,任何脚本都在应用程序的主线程上运行。

    因此,您的脚本是自阻塞的。该脚本在初始化对话框并发送消息以显示它之后,阻塞了主线程。因此无法执行正确绘制对话框的消息,并且无法正确显示对话框。 但是,当脚本等待来自对话框的消息时,您处于阻塞状态。

    您不能将对话框放在单独的线程上 - 您只能将脚本启动对话框放在单独的线程上。 UI 总是需要一些主线程周期才能真正工作。

    因此,您真正需要做的是确保脚本不在主线程上。 最简单的方法是使用 old 方法将脚本执行放在单独的线程上,方法是将脚本的第一行 exactly 写为如下:

    // $BACKGROUND$
    

    // 后面的全部大写和一个空格) 如果您将这一小行添加到脚本中,则一切正常。 在单独的线程上启动脚本的更优雅的方法是使用线程类,就像您在 版本 2 中所做的那样。只需确保将脚本等待信号放在后台线程上,而不是对话框上。

    【讨论】:

    • 啊哈,这就是它不起作用的原因。完美的解释加上一个可行的解决方案。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-18
    • 2016-04-21
    相关资源
    最近更新 更多