Graphical User Interface

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
papux
In need of some credit
In need of some credit
Posts: 5
Joined: Mon Feb 21, 2005 10:37 am

Graphical User Interface

Post by papux »

hello i have only one question... can i use wx to create a gui for a command line tool like tar? i mean in a terminal can i type "tar -xf archive.tar" i will that my gui do this for me, i think i have found the funktions in wx they called;
"Process control functions"
::wxExecute
::wxExit
::wxKill
::wxGetProcessId
::wxShell
::wxShutdown

p.s.: sorry for my english im german...
-----------------------
m.f.g
dennis
-----------------------
iBook G4 12" 1,2GHz 256MB
MacOSX 10.3.8
upCASE
Moderator
Moderator
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE »

Hi!
hello i have only one question... can i use wx to create a gui for a command line tool like tar?
Yes, you can.
For further questions: Send me a PM in german if you like.
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda
User avatar
ABX
Can't get richer than this
Can't get richer than this
Posts: 810
Joined: Mon Sep 06, 2004 1:43 pm
Location: Poznan, Poland
Contact:

Post by ABX »

upCASE wrote:For further questions: Send me a PM in german if you like.
Just a minor note (thought not private becase is can be related to all readers) shortcuts like 'PM' are out of context (senseless) for those of us who is reading this forum through RSS :-) Just in case you could be surprised by somebody in the future who did not followed your advice because missed its meaning :-)

ABX
CVS Head, 2.8.X
wxMSW, wxWinCE, wxPalmOS, wxOS2, wxMGL, bakefile
gcc 3.2.3, bcc 5.51, dmc 8.48, ow 1.6, vc 7.1, evc 3/4, pods 1.2
Vexator
I live to help wx-kind
I live to help wx-kind
Posts: 187
Joined: Sun Jan 30, 2005 2:50 pm
Location: Heidelberg, Germany

Post by Vexator »

i have the same issue.. would be nice if you'd share your knowledgle with all of us, dear mod :)
upCASE
Moderator
Moderator
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE »

Hi!
I answered the original question in a private message in the forum (located at http://forums.wxwidgets.org/ (Ok ABX?? :D :D )).
Here's a quick outtake and answer:

For simply starting a program and capturing its output use

Code: Select all

 long wxExecute(const wxString& command, wxArrayString& output, wxArrayString& errors)
"command" is the command to use with all needed arguments, output and errors are Arrays storing the returned lines from stdout and stderr.

If you have a more complex task and need the program running asynchronously in background, use

Code: Select all

long wxExecute(const wxString& command, int sync = wxEXEC_ASYNC, wxProcess *callback = NULL)
You'll have to use a wxProcess and capture its OnTerminate() event. You can get the in- and output by using wxProcess::GetInputStream() and wxProcess::GetOutputStream() .
Use

Code: Select all

wxInputStream* stream = myProcess->GetInputStream();
        wxTextInputStream tis(*stream);
        wxString msg;
        msg <<  tis.ReadLine();
for example to read the first line of the programs stdout output.
To "live" capture the output of a longer running task, catch OnIdle() events and check if input is available for the stream.

For a nice example check the "exec" sample in the sample folder. The menu "Exec"->"Capture command output" shows what you want. Synchronous execution shows the first thing I mentioned, asynchronous execution the second case.
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda
chocobo_ff
Earned a small fee
Earned a small fee
Posts: 17
Joined: Tue Jan 10, 2006 12:52 am
Location: New Zealand

Post by chocobo_ff »

upCASE wrote: To "live" capture the output of a longer running task, catch OnIdle() events and check if input is available for the stream.
Hi,

I have a code as follow which tries to get output from a console program (called console.exe) and display it in a wxTextCtrl, but I can only get the first output.... was reading this post which I found extremely helpful, but was wondering if I can get some guidance on how to do this (capture the output of a longer running task)? I tried to look at the sample/exec example, but couldn't follow that at all :(

Code: Select all

void main::OnButtonRunClick( wxCommandEvent& event )
{ 
	wxString cmd = "console.exe";

	wxProcess *process = new wxProcess(this);
	process->Redirect();

	if (wxExecute(cmd, wxEXEC_ASYNC, process))
	{
		wxString msg;
		wxInputStream *iStream = process->GetInputStream();
		wxTextInputStream tStream(*iStream);
		msg = tStream.ReadLine();
		mTextCtrl->AppendText(msg);
	}

	event.Skip();
}
Many thanks in advance :D
upCASE
Moderator
Moderator
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE »

Have a look at the exec example. It implements OnIdle() for reading the output of async processes.
The example uses a derived class (MyPipedProcess), but for only one process you can do that with wxProcess itself.

Implement OnIdle(). Start the process like you did. In OnIdle(), check if IsInputAvailable() returns true for the process. Then use GetInputStream() to read the input. In the same way hanlde IsErrorAvailable() and GetErrorStream().
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda
chocobo_ff
Earned a small fee
Earned a small fee
Posts: 17
Joined: Tue Jan 10, 2006 12:52 am
Location: New Zealand

Post by chocobo_ff »

upCASE wrote:Implement OnIdle(). Start the process like you did. In OnIdle(), check if IsInputAvailable() returns true for the process. Then use GetInputStream() to read the input. In the same way hanlde IsErrorAvailable() and GetErrorStream().
I'm guessing in order to be able to use the variable which I've called "process" in my OnIdle(), I'd need to define "process" outside of my "OnButtonRunClick()" function right? Ok now this is going to be a really stupid question, but where would I define my "process"? At the moment I have 2 files, one called "main.h" and one called "main.cpp", in "main.h" I've defined my

Code: Select all

class main : public wxDialog
{
    DECLARE_DYNAMIC_CLASS( main )
    DECLARE_EVENT_TABLE()

public:
    /// Constructors
    main( );
    main( wxWindow* parent, wxWindowID id = SYMBOL_MAIN_IDNAME, const wxString& caption = SYMBOL_MAIN_TITLE, const wxPoint& pos = SYMBOL_MAIN_POSITION, const wxSize& size = SYMBOL_MAIN_SIZE, long style = SYMBOL_MAIN_STYLE );

	...
	wxProcess *process;
};
then I got

Code: Select all

main::main( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
	process = new wxProcess(this);
	process->Redirect();
	Create(parent, id, caption, pos, size, style);
}
and finally I have

Code: Select all

void main::OnButtonRunClick( wxCommandEvent& event )
{
        wxString cmd = "console.exe";

        if (wxExecute(cmd, wxEXEC_ASYNC, process))
        {
                wxString msg;
                wxInputStream *iStream = process->GetInputStream();
                wxTextInputStream tStream(*iStream);
                msg = tStream.ReadLine();
                mTextCtrl->AppendText(msg);
        }

        event.Skip();
}
Obviously I did something really stupid here cos when I run the program (it compiled fine), it gave me a "Debug Error!... DAMAGE: after Normal block (#2323) at 0x00DD21A8..."

Any help is much appreciated :)

PS: I'm using MSVC 7.1, and I used DialogBlocks to generated part of the codes
upCASE
Moderator
Moderator
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE »

chocobo_ff wrote:

Code: Select all

main::main( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
	process = new wxProcess(this);
	process->Redirect();
	Create(parent, id, caption, pos, size, style);
}
Obviously I did something really stupid here cos when I run the program (it compiled fine), it gave me a "Debug Error!... DAMAGE: after Normal block (#2323) at 0x00DD21A8..."
Don't create the process before the Create() function wass called. I'd create the process in the OnButton() method if it doesn't exist allready. Set it to NULL in the constructor and in the OnButton() method do something like

Code: Select all

if(!process)
{    process = new wxProcess(this);
    process->Redirect();
}
Also check if the input stream is != NULL.
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda
chocobo_ff
Earned a small fee
Earned a small fee
Posts: 17
Joined: Tue Jan 10, 2006 12:52 am
Location: New Zealand

Post by chocobo_ff »

upCASE wrote: Don't create the process before the Create() function wass called. I'd create the process in the OnButton() method if it doesn't exist allready. Set it to NULL in the constructor and in the OnButton() method do something like

Code: Select all

if(!process)
{    process = new wxProcess(this);
    process->Redirect();
}
Also check if the input stream is != NULL.
Ok I will try that first thing tomorrow morning and let you guys know how I'm going with it. Just another question while its on my mind, would I do similar thing in OnIdle(), i.e. check if process has been created and input stream is != NULL ? The problem I had before is that the process would be called at OnIdle() straight after the program starts, and that crashes the program, so that's why I tried to create the process before Create()?
upCASE
Moderator
Moderator
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE »

chocobo_ff wrote:Ok I will try that first thing tomorrow morning and let you guys know how I'm going with it. Just another question while its on my mind, would I do similar thing in OnIdle(), i.e. check if process has been created and input stream is != NULL ? The problem I had before is that the process would be called at OnIdle() straight after the program starts, and that crashes the program, so that's why I tried to create the process before Create()?
Exactly. Check if the process exists, if it has input and if the input is not NULL.
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda
chocobo_ff
Earned a small fee
Earned a small fee
Posts: 17
Joined: Tue Jan 10, 2006 12:52 am
Location: New Zealand

Post by chocobo_ff »

upCASE wrote: Don't create the process before the Create() function wass called. I'd create the process in the OnButton() method if it doesn't exist allready. Set it to NULL in the constructor and in the OnButton() method do something like

Code: Select all

if(!process)
{    process = new wxProcess(this);
    process->Redirect();
}
Also check if the input stream is != NULL.
Ok I just tried to put the code you showed above in my program... and it crashed when I run it, so I tried to go to debug mode, and when it goes to the line

Code: Select all

if (!process)
for the first time, instead of going into the code it jumps to the next line (after the whole (!process) part), is this because I've got

Code: Select all

 wxProcess *process;
in my class definition? Or is it because of something else?
upCASE
Moderator
Moderator
Posts: 3176
Joined: Mon Aug 30, 2004 6:55 am
Location: Germany, Cologne

Post by upCASE »

Did you initialize process to NULL??
OS: OpenSuSE, Ubuntu, Win XP Pro
wx: svn
Compiler: gcc 4.5.1, VC 2008, eVC 4

"If it was hard to write it should be hard to read..." - the unknown coder
"Try not! Do. Or do not. There is no try." - Yoda
chocobo_ff
Earned a small fee
Earned a small fee
Posts: 17
Joined: Tue Jan 10, 2006 12:52 am
Location: New Zealand

Post by chocobo_ff »

upCASE wrote:Did you initialize process to NULL??
Sorry completely missed that :( I've done it just before you replied and it sort of works now, but the problem is that it crashes in the OnIdle() part after a while, I think whats happening is the console program I'm calling is finished before my wxWidgets GUI gets all the outputs, so it only gets say the first 3 out of the 10 outputs.

Code: Select all

if (mProcess)
	{
		if (mProcess->IsInputAvailable())
		{
			wxString msg;
			wxInputStream *stream = mProcess->GetInputStream();
			wxTextInputStream tStream(*stream);
			msg = tStream.ReadLine();
			mTextCtrl->AppendText(msg + "\n");
		}
	}
Also (I think) when the console program finishes, in OnIdle() it still calls "if (mProcess)", and when the console program has finished, the mProcess still exists and isn't NULL, but it's not what I wanted, and so that's probably why it crashed?? Also now I'm a bit confused as to what you meant when you said "check if the input is not NULL" ... is that referring to the "mProcess->IsInputAvailable()" part != NULL? Many thanks and sorry for taking up your time

P.S.: A bit about my console program - it takes a value from my wxWidgets program, say 15, and then it runs from 0 - 14 (total of 15 numbers) with a 100ms pause in between.
Sickboy
Experienced Solver
Experienced Solver
Posts: 91
Joined: Wed Mar 16, 2005 10:30 pm
Location: Germany

Post by Sickboy »

Just put a cout<<"myApp has been finished";getchar(); in your console application before it should return 0.
And in the pipe redirect you check each line for the string: "myApp has been finished". If it appears printf or cout a "\r\n" through the pipe to the console app so that it can finish...
Post Reply