如何在 dm-script 中创建包含两个以上按钮的阻塞对话框

How to create a blocking dialog with more than two buttons in dm-script

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


可以使用 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块,注释所有其他版本

但这不起作用。出现的对话框还没有完成,看起来像下面这样:

我一更改 window,对话框就会显示文本和按钮,但仍然不可点击。再加上布局不完善

版本 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");

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

All UI actions in DigitalMicrograph are run on the main thread of the application.

还有:

Any script is, by default, run on the main thread of the application.

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

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

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

// $BACKGROUND$

(全部大写,// 后加白色 space) 如果将这一行添加到脚本中,一切正常。 在单独的线程上启动脚本的一种更优雅的方法是使用线程 class,就像您在 版本 2 中所做的那样。只需确保将脚本 waiting for the signal 放在后台线程,而不是对话框。