From: crjjrc on
I think I've got a solution for making a hybrid application that can
either be run in GUI or console mode, depending on command-line
parameters. However, I've been unable to find an example of such a
program using wxWidgets. wx-users contains much discussion about
this, but I'm failing to find any code.

My solutions follows. If any command-line parameter is passed, no GUI
is used. (The check is a bit of a hack on MSW, I know.) Any thing
I'm doing wrong?

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

class GUITest : public wxApp {
bool OnInit();
};

bool GUITest::OnInit() {
wxFrame *frame = new wxFrame(NULL, -1, _T("Test"),
wxDefaultPosition,
wxDefaultSize);
frame->Show();
SetTopWindow(frame);
return true;
}

class GUIlessTest : public wxAppConsole {
bool OnInit();
};

bool GUIlessTest::OnInit() {
std::cout << "guiless" << std::endl;
return false;
}

#ifdef __WXMSW__
extern "C" int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
wxCmdLineArgType lpCmdLine,
int nCmdShow) {
int argc = 2 * lpCmdLine;
#else
int main(int argc, char **argv) {
#endif
if (argc == 2) {
GUIlessTest *app = new GUIlessTest;
} else {
GUITest *app = new GUITest;
}
#ifdef __WXMSW__
return wxEntry(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
#else
return wxEntry(argc, argv);
#endif
}
From: Vadim Zeitlin on
On Fri, 11 Apr 2008 08:28:22 -0700 (PDT) crjjrc <crjjrc(a)gmail.com> wrote:

c> My solutions follows. If any command-line parameter is passed, no GUI
c> is used. (The check is a bit of a hack on MSW, I know.) Any thing
c> I'm doing wrong?

I don't think you do anything wrong but it would be nicer (e.g. if you
intend to put this on the wiki or maybe submit for inclusion in the manual
or as a new sample) if wx command line parsing could be used, is it too
late to do this in OnCmdLineParsed()?

Also, writing GUIlessTest seems rather pointless, usually people want to
have their "main()" called if they don't use GUI so you could just replace
this class creation with calls to wxInitialize (or use wxInitializer) and
call some console_main() function.

Regards,
VZ

--
TT-Solutions: wxWidgets consultancy and technical support
http://www.tt-solutions.com/

From: crjjrc on
On Apr 11, 9:36 pm, Vadim Zeitlin <va...(a)wxwidgets.org> wrote:
> On Fri, 11 Apr 2008 08:28:22 -0700 (PDT) crjjrc <crj...(a)gmail.com> wrote:
>
> c> My solutions follows. If any command-line parameter is passed, no GUI
> c> is used. (The check is a bit of a hack on MSW, I know.) Any thing
> c> I'm doing wrong?
>
> I don't think you do anything wrong but it would be nicer (e.g. if you
> intend to put this on the wiki or maybe submit for inclusion in the manual
> or as a new sample) if wx command line parsing could be used, is it too
> late to do this in OnCmdLineParsed()?

Considering that OnCmdLineParsed() is a member of wxAppConsole, I
don't think this could be done since we are using the command-line
arguments to figure which application to create. Or am I missing
something?

Alternately I can just trigger the parsing myself by instancing a
wxCmdLineParser in main() or WinMain() and calling Parse(), right?
I'm not sure how to get the appropriate argc and argv for the parser's
constructor though. On Windows, I get one string in lpCmdLine, which
doesn't include the program name. Does wxCmdLineParser::SetCmdLine()
expect the program name to be first? It doesn't say in the docs.
Everywhere else, the incoming argv is char ** and the constructor
expects wxChar **. Is there an easy way to convert this? wxApp*
objects set their own argc and argv somewhere behind the scenes, and
I'm not sure how they do it.

- Chris
From: crjjrc on
On Apr 14, 2:53 pm, Vadim Zeitlin <va...(a)wxwidgets.org> wrote:

> c> Considering that OnCmdLineParsed() is a member of wxAppConsole, I
> c> don't think this could be done since we are using the command-line
> c> arguments to figure which application to create. Or am I missing
> c> something?
>
> I thought about deriving a wxGUIOrConsoleApp class from wxApp (i.e. the
> GUI version) but overriding its virtual Initialize() to call wxAppConsole
> version if argc != 1. I didn't test it but I think it should be possible to
> make this work.

I like this idea quite a bit. I found I had a bit more to do to get
it to work, and more things probably remain. I had to override
wxApp::CleanUp() since it too fiddled with GTK. And I had to make
sure the active log target was stderr instead of wxLogGui. It seemed
the most correct way to do this was derive a new wxAppTraits class and
override CreateLogTarget().

Here's the code, which now uses wxCmdLineParser:

------------
#include <iostream>
#include <wx/wx.h>
#include <wx/apptrait.h>
#include <wx/cmdline.h>

class HybridTraits : public wxGUIAppTraits {
public:
HybridTraits(bool gui_enabled)
: wxGUIAppTraits(),
gui_enabled(gui_enabled) {
}

wxLog *CreateLogTarget() {
if (gui_enabled) {
return wxGUIAppTraits::CreateLogTarget();
} else {
return new wxLogStderr;
}
}

private:
bool gui_enabled;
};

class GUITest : public wxApp {
private:
bool OnInit();

bool Initialize(int& argc, wxChar **argv) {
static const wxCmdLineEntryDesc desc[] = {
{ wxCMD_LINE_SWITCH, "c", "console", "run in console
mode" },
{ wxCMD_LINE_NONE }
};

wxCmdLineParser parser(desc, argc, argv);
if (parser.Parse(true) != 0) {
exit(1);
}

gui_enabled = !parser.Found("c");
if (gui_enabled) {
return wxApp::Initialize(argc, argv);
} else {
return wxAppConsole::Initialize(argc, argv);
}
}

void CleanUp() {
if (gui_enabled) {
wxApp::CleanUp();
} else {
wxAppConsole::CleanUp();
}
}

HybridTraits *CreateTraits() {
return new HybridTraits(gui_enabled);
}

bool gui_enabled;
};

bool GUITest::OnInit() {

wxString msg;

if (gui_enabled) {
msg = "in gui mode";
} else {
msg = "in console mode";
}

wxLogMessage(msg);
return false;

}

IMPLEMENT_APP(GUITest);

------------

Vadim, if you think this would make a good sample, I'm happy to make
any changes to get it to a presentable state.

> You can use wxCmdLineParser::ConvertStringToArgs() just as the code in
> src/msw/main.cpp does. But, again, hopefully this is unnecessary anyhow.

Good to know. Thanks!

- Chris
From: crjjrc on
On Apr 15, 12:39 pm, Vadim Zeitlin <va...(a)wxwidgets.org> wrote:

> c> I like this idea quite a bit. I found I had a bit more to do to get
> c> it to work, and more things probably remain. I had to override
> c> wxApp::CleanUp() since it too fiddled with GTK.
>
> Sorry, what exactly was the problem? I think we should fix it in wxGTK.

Well, the assertion failure occurs in wxApp::CleanUp in src/gtk/
app.cpp. At last checkout, this was line 529.

void wxApp::CleanUp()
{
if (m_idleSourceId != 0)
g_source_remove(m_idleSourceId);

// release reference acquired by Initialize()
g_type_class_unref(g_type_class_peek(GTK_TYPE_WIDGET)); // line
529

However, I don't think we can say it constitutes a bug. If I override
Initialize(), in which the GTK libraries are dynamically loaded and
which fails if they're not, I should probably expect that I need to
override any other wxApp functions that assume GTK is available.

> Yes, I think this is a useful example and I'd be favourable to adding it
> as a new sample. I don't have a good name for it though ("hybrid" is too
> generic), do you?

guioptional? guinogui?

I'm not sure how to address any Windows issues, since I don't have a
Windows machine to build on. It was my understanding that wApps do
not show the console window at all, which would be the case here.
wxAppConsoles, on the other, optionally show the console window.

- Chris