【问题标题】:Closing a frame in Java and opening another one, with the swt library from eclipse使用 Eclipse 中的 swt 库在 Java 中关闭一个框架并打开另一个框架
【发布时间】:2022-01-27 05:15:17
【问题描述】:

我目前正在用 Java 制作一个阅读应用程序,这是my main menu

我想要的是,当我在选择一本书后按下底部按钮时,会打开另一个带有该书的窗口。我现在所做的是一个功能,它会在关闭我当前所在的窗口的同时打开另一个窗口以释放一点内存。 在从中检索到必要的信息(书的索引)后,我首先关闭当前窗口,如下所示:

btnOuvrirLeLivre.addSelectionListener(new SelectionAdapter() {
    @Override
    public void widgetSelected(SelectionEvent e) {
        int index = list.getSelectionIndex();
        LiseuseController controller = new LiseuseController(null, null);
        parent.dispose();
        controller.viewBookController(index);       
    }
});

(我的项目使用的是MVC方法),parent只是用于打开框架的复合体。

public void viewBookController(int index) {
    Display displayBook = new Display();
    Shell shellBook = new Shell(displayBook);
    shellBook.setLayout(new GridLayout(1, false));
    index += 250;
    LiseuseView lecture = new LiseuseView(shellBook, SWT.NONE, index);
    shellBook.pack();
    shellBook.open();
    while(!shellBook.isDisposed()) {
        if(!displayBook.readAndDispatch())
            displayBook.sleep();
    }
    displayBook.dispose();
}

索引只是我数据库中的书号,一切都应该没问题,但是在按下按钮打开书后执行此操作时出现此错误:

Exception in thread "main" org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:4875)
at org.eclipse.swt.SWT.error(SWT.java:4790)
at org.eclipse.swt.SWT.error(SWT.java:4761)
at org.eclipse.swt.widgets.Display.checkDisplay(Display.java:824)
at org.eclipse.swt.widgets.Display.create(Display.java:887)
at org.eclipse.swt.graphics.Device.<init>(Device.java:126)
at org.eclipse.swt.widgets.Display.<init>(Display.java:563)
at org.eclipse.swt.widgets.Display.<init>(Display.java:554)
at Controller.LiseuseController.viewBookController(LiseuseController.java:133)
at View.LiseuseHome$4.widgetSelected(LiseuseHome.java:81)
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:252)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:89)
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4209)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1037)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4026)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3626)
at Controller.LiseuseController.viewController(LiseuseController.java:126)
at Main.Main.main(Main.java:59)

(LiseuseController.java:133) 包含“Display displayBook = new Display();”

并且 (LiseuseController.java:126) 包含

if(!display.readAndDispatch())
            display.sleep();

这是用于打开第一个窗口的另一个函数。

我真的不明白是什么导致了这个错误,我不能像使用 Jframe 一样将一个置于“可见”而另一个置于“不可见”,因为这两个窗口位于 2 个不同的 .java 上文件。

你知道如何解决这个问题吗?

【问题讨论】:

  • 您应该只在应用程序启动时创建一个Display。此外,您必须在单个主线程上执行 所有 UI 操作,看起来您正在尝试为 UI 使用多个线程。仅凭一些代码很难说更多。

标签: java eclipse user-interface swt


【解决方案1】:

来自org.eclipse.swt.widgets.DisplayAPI documentation

使用 SWT 构建的应用程序几乎总是只需要 单个显示器。特别是一些 SWT 支持的平台 将不允许超过一个活动显示。换句话说,有些 如果已经存在,平台不支持创建新显示 尚未发送 dispose() 消息。

因此,您应该调整您的应用程序以仅使用Display 的一个实例。由于类 LiceuseController 已经管理打开初始窗口和补充窗口,它似乎适合管理 Display 的实例以供两种用途。

此实例应在应用程序启动时创建,由LiceuseController 类维护,并在应用程序关闭时最终释放。


另一个问题是LiceuseController 中的方法实际上都没有返回。从堆栈跟踪中可以看出,当您创建新窗口时,来自LiseuseController.viewControllerDisplay.readAndDispatch 仍然处于活动状态。我猜您还想在关闭辅助窗口时重新打开原始窗口。但是,以这种方式打开和关闭窗口会不断增加您的调用堆栈,直到您最终得到 StackOverflowException

相反,LiceuseController 应该能够在不受外界干扰的情况下创建窗口。因此,与其主动调用控制器类中的方法来打开另一个窗口,侦听器应该只告诉控制器在当前窗口关闭时它应该下一个打开哪个窗口.

一个例子可能看起来像

public enum WindowType {
    MAIN, BOOK, NONE
}

public class LiceuseController {

    Display display;
    WindowType nextToOpen = WindowType.MAIN;

    public LiceuseController() {
        display = new Display();
    }

    public void setNextToOpen(WindowType value) {
        nextToOpen = value;
    }

    public void run() {
        for (boolean run = true; run;) {
            switch(nextToOpen) {
            case MAIN:
                viewController();
                break;
            case BOOK:
                viewBookController();
                break;
            case NONE:
                run = false;
                break;
            default:
                throw new RuntimeException("unexpected enum constant");
            }
        }
        /*
         * Depending on how you want to manage the instance of this class,
         * you could also extract this into a separate method.
         */
        display.dispose();
    }

    private void viewController() {
        // open main window using 'display'
    }

    private void viewBookController() {
        // open book window using 'display'
    }

}

这样监听者只需要调用LiceuseController.setNextToOpen然后关闭当前窗口。这将导致viewControllerviewBookController 返回,之后循环将重新进入并打开请求的窗口。要关闭应用程序,请使用WindowType.NONE 调用setNextToOpen

【讨论】:

    猜你喜欢
    • 2020-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多