【问题标题】:Standalone FileDialog window with wxWidgets带有 wxWidgets 的独立 FileDialog 窗口
【发布时间】:2015-02-25 02:35:24
【问题描述】:

我正在尝试使用 wxWidgets 以跨平台方式创建打开/保存 FileDialog 窗口。所以我查看了the documentation 中的示例。我还想创建没有父窗口的独立窗口,因为我没有在程序的其他地方使用 wxApp/wxWindow 的任何其他实例。

另外,我需要有自己的main函数,所以我不想使用IMPLEMENT_APP之类的宏。我尝试按照here 给出的说明进行操作,并提出了以下最小程序:

#include <wx/wx.h>
#include <string>
#include <iostream>

std::string openFile() {
    wxFileDialog openFileDialog(NULL,  _("Open XYZ file"), "", "",
        "XYZ files (*.xyz)|*.xyz", wxFD_OPEN|wxFD_FILE_MUST_EXIST);

    if (openFileDialog.Show() == wxID_CANCEL)
        return ""; // the user changed idea...

    // proceed loading the file chosen by the user;
    return "something";
}

int main(int argc, char *argv[]) {
    std::cout << wxEntryStart(argc, argv) << std::endl;
    std::string s = openFile();
    wxEntryCleanup();
}

这是我用来编译代码的 CMakeLists.txt:

CMake_Minimum_Required(VERSION 2.8.11)
Project(test)

Find_Package(wxWidgets REQUIRED)
Include(${wxWidgets_USE_FILE})

Add_Executable(test main.cpp)
Target_Link_Libraries(test ${wxWidgets_LIBRARIES})

尽管如此,当我运行这个程序时,我得到一个分段错误,尽管wxEntryStart 返回true,我不知道问题出在哪里。有什么建议吗?

【问题讨论】:

  • 首先构建一个调试版本并在调试器中运行以查看在哪里发生崩溃。
  • 如果我在调用wxEntryStart 之前添加一行包含wxApp::SetInstance( new wxApp() ); 的行,它就不会再崩溃了。但是,当我运行程序时,没有提示任何窗口,并且openFileDialog.Show() 行立即返回(openFile 返回"something")。

标签: c++ wxwidgets


【解决方案1】:

我不会那么大胆地​​剥离 wx 的初始化代码。它今天可能工作,但在下一个版本中,谁知道......

这是我用的:

class MyApp : public wxApp { };
wxIMPLEMENT_APP_NO_MAIN(MyApp);

int main()
{
    wxDISABLE_DEBUG_SUPPORT();
    int dummy = 0;
    if(!wxEntryStart(dummy, static_cast<wxChar**>(nullptr)))
        return 1;
    auto onx1 = on_exit([]{ wxEntryCleanup(); }); //using RAII for cleanup

    //Other initialization, create main window, show it.

    wxGetApp().OnRun(); //This starts the event loop.
    //In your case, it looks like ShowModal's event loop is enough, 
    //so you don't need this.
}

我认为那些丑陋的宏可以更好地隔离库初始化代码的未来变化。

【讨论】:

  • 如果我们在同一个文件中使用 wxIMPLEMENT_APP_NO_MAIN 是否需要 wxDECLARE_APP?我们可以使用 wxIMPLEMENT_APP_NO_MAIN(wxApp) 并避免声明我们自己的子应用类吗?
  • @skuld 你说得对,wxDECLARE_APP 只声明了wxGetApp(),这也是在另一个宏中定义的。我想它也应该适用于 wxApp,但我更喜欢让事情尽可能接近“官方”的做事方式。一旦我在一个文件中接受了几个丑陋的宏,再一个空的类定义就没什么大不了了。
  • 我将从代码示例中删除 wxDECLARE_APP(),以方便未来的读者。
  • 听起来不错。但是我会坚持使用wxApp 而不是MyApp,因为文档没有建议不要这样做。如果我看到我需要一个 MyApp 子类,我将来会改变主意,尽管我看不出用空的 MyApp 可能会起作用,并且在使用基类时会被破坏。
  • @skuld 我也倾向于认为wxApp 没问题,但将来打破它的一种方法是在 wx.xml 中通过某些宏引入双重定义。例如,假设一个函数的默认版本将在 wx 代码中定义,采用 wxApp 类型的参数,然后 wx 宏将根据您传入的应用程序名称定义一个新版本;如果你传入wxApp,你会突然有重复的定义。我会第一个说这是一个极不可能发生的情况,但当我把 MyApp 留在里面时,我就是这么想的。
【解决方案2】:

好的,经过一番摆弄之后,这是一个适合我的代码示例。欢迎就最佳实践发表评论。我所做的是在openFile 函数中保留ShowModal() 而不是Show()。我还创建了单例wxApp 的实例。最终代码在这里:

#include <wx/wx.h>
#include <string>
#include <iostream>

std::string openFile() {
    wxFileDialog openFileDialog(NULL,  _("Open XYZ file"), "", "",
        "XYZ files (*.xyz)|*.xyz", wxFD_OPEN|wxFD_FILE_MUST_EXIST);

    if (openFileDialog.ShowModal() == wxID_CANCEL)
        return ""; // the user changed idea...

    // proceed loading the file chosen by the user;
    return "something";
}

int main(int argc, char *argv[]) {
    wxApp::SetInstance( new wxApp() );
    wxEntryStart(argc, argv);
    std::string s = openFile();
    wxEntryCleanup();
}

不确定这是否完全没有泄漏,因为valgrind 在退出后似乎有点抱怨。关于我是否也可以将wxEntryStart() 放在openFile() 函数欢迎中的任何提示(我保证这是唯一使用 wxWidgets 库的地方,我希望 API 尽可能简单)。

【讨论】:

  • 在不太了解 wxWidgets 的情况下,Show 函数可能需要运行消息循环才能工作,而 ShowModal 包含它自己的消息循环,用于自己的窗口。
猜你喜欢
  • 2015-12-31
  • 2011-03-13
  • 2014-07-18
  • 2010-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-05
  • 1970-01-01
相关资源
最近更新 更多