0

I'm trying to use wxWidgets to create a open/save FileDialog window in a cross-platform way. So I've looked at the examples in the documentation. I also want to create standalone windows, with no parent, because I am not using any other instance of a wxApp/wxWindow elsewhere in my program.

Additionally, I need to have my own main function, so I don't want to use a macro such as IMPLEMENT_APP. I tried to follow the instructions given here, and came up with the following minimal program:

#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();
}

And here is the CMakeLists.txt I used to compile the code:

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})

Still, when I run this program, I get a Segmentation Fault, despite the wxEntryStart returning true, and I have no idea where the problem comes from. Any tip?

Community
  • 1
  • 1
skuld
  • 149
  • 1
  • 8
  • Start by building a debug version and run in a debugger to see *where* the crash happens. – Some programmer dude Dec 28 '14 at 19:30
  • It doesn't crash anymore if I precede the call to `wxEntryStart` by a line that contains `wxApp::SetInstance( new wxApp() );`. However when I run the program no window is prompted, and the `openFileDialog.Show()` line returns immediately (`openFile` returns `"something"`). – skuld Dec 28 '14 at 19:36

2 Answers2

0

Ok, after some fiddling here is a code sample that works for me. Comments welcome on what'd be the best practices. What I did was keep ShowModal() instead of Show() in the openFile function. I also created the instance of singleton wxApp. The final code is 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.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();
}

Not sure this is completely leak-free, as valgrind seems to complain a little after exit. Any tip about whether I could also put the wxEntryStart() in the openFile() function welcome (I am guaranteed that this is the only place where the wxWidgets lib is used, and I want an API as simple as possible).

skuld
  • 149
  • 1
  • 8
  • 1
    Without knowing wxWidgets that well, the `Show` function probably needs a message loop running to work, while `ShowModal` contains it own message loop for its own window. – Some programmer dude Dec 28 '14 at 19:53
0

I wouldn't be so bold with stripping down wx's initialization code. It may work today, but in the next version, who knows...

This is what I use:

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.
}

I think those macros, ugly as they are, provide much better insulation against future changes in the library initialization code.

bogdan
  • 9,229
  • 2
  • 33
  • 48
  • Do we need wxDECLARE_APP if we use wxIMPLEMENT_APP_NO_MAIN in the same file? Can we use wxIMPLEMENT_APP_NO_MAIN(wxApp) and avoid declaring our own child app class? – skuld Jan 02 '15 at 12:43
  • @skuld You're right, `wxDECLARE_APP` only declares `wxGetApp()`, which is also defined in the other macro. I guess it should also work with wxApp, but I prefer to keep things as close to the 'official' way of doing things as possible. Once I accepted a couple of ugly macros in a file, one more empty class definition is no big deal. – bogdan Jan 02 '15 at 13:54
  • I'll remove `wxDECLARE_APP()` from the code sample, for the benefit of future readers. – bogdan Jan 02 '15 at 14:03
  • Sounds good to me. However I'll stick with using `wxApp` instead of `MyApp`, as the documentation doesn't advise against that. I'll change my mind in the future if I see I need a `MyApp` child class, albeit I don't see what could possibly work with an empty `MyApp` and be broken when using the base class instead. – skuld Jan 02 '15 at 19:04
  • @skuld I, too, tend to think you'll be alright with `wxApp`, but a way for it to break in the future would be to have double definitions introduced by some macro in wx. For example, let's say a default version of a function would be defined in the wx code, taking a parameter of type `wxApp`, and then a wx macro would define a new one based on the app name you passed in; if you passed in `wxApp`, you'd suddenly have duplicate definitions. I'll be the first to say this is a highly unlikely scenario, but that's what I was thinking when I left that `MyApp` in there. – bogdan Jan 02 '15 at 19:41