Discussion:
[fltk.general] passing things from terminal to GUI
Greg Ercolano
2007-06-03 18:51:36 UTC
Permalink
With some studying on this program and FLUID code I'll make,
I think I'll get things down before too long.
Yes; Fluid should be able to also make code just like this
as well; it's well designed for it.

I have several commercial programs managed by fluid, one
that's quite large and complex, full of custom widgets,
dialogs, multiple screens. Here's the fluid code browser for that app:
Loading Image...

There's plenty of manually maintained code that this app calls,
so not /all/ the code is maintained by fluid, just the widget-y stuff.
Some files are just easier to maintain as a text file, and then either
#include into the fluid app, or reference as modules.

For instance, I used to have all my #includes in fluid.
But it was getting too large and took up a lot of screen space
in fluid's browser, so I moved it all into a separate .h file,
and just have a single #include in fluid to load it all in.

Fluid's been great.
On a side note, do you plan on making any more FLTK videos?
They are a HUGE help and I'd love to get into some class stuff
Yes, I'd like to do a few more, but it's been hard to get
into the swing of it again.

One of the videos does cover the subject covered here
of how to turn a "C" style FLTK demo into a "C++" style program.
I think the last part of the "Hello World" video touches on that.

That subject should probably be covered more in an advanced video.
It's a broad subject, has to be planned out. That's what's been
taking the most time is to know where to start. Over the years
I've been taking notes how to break it out, but just haven't been
motivated to take it on.
adam
2007-06-02 05:37:00 UTC
Permalink
I'm looking for a function similar to the wxExecute function. http://www.wxwidgets.org/wiki/index.php/WxExecute this allows you to do command line stuff without using the command line.

I've tried using the popen example from Greg Ercolano, but its not working. I am calling a console program from my GUI. I want to run the entire thing without seeing the console window at all, running the program in a box in my GUI. Most programming IDEs use this when compiling and stuff. That's the effect I want, however, i can't seem to figure this out.

The Popen example mentioned earlier uses fgets to read the stream, however, that will only read parts of the stream. when i run the console programs using popen, i get SOME text in the GUI window, but some text is still sent to the console window.

Any tips would be appreciated, I am a newbie at this stuff.

Thanks,
Adam
Greg Ercolano
2007-06-02 07:23:27 UTC
Permalink
Post by adam
I'm looking for a function similar to the wxExecute function. http://www.wxwidgets.org/wiki/index.php/WxExecute this allows you to do command line stuff without using the command line.
I've tried using the popen example from Greg Ercolano, but its not working.
What problem are you having?

I just compiled it again on my Visual Studio 6 and it ran fine.
In this case with 1.1.6:

C:\fltk-1.1.6\test>cl /I "." /I ".." /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "WIN32_LEAN_AND_MEAN" /D "VC_EXTRA_LEAN" /D "WIN32_EXTRA_LEAN" /EHsc /MD /TP /c foo.cxx
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.00.9466 for 80x86
Copyright (C) Microsoft Corporation 1984-2001. All rights reserved.

foo.cxx

C:\fltk-1.1.6\test>link /OUT:foo.exe /INCREMENTAL:NO /NOLOGO /LIBPATH:"..\lib" /NODEFAULTLIB:"libcd" /SUBSYSTEM:CONSOLE /MACHINE:I386 glu32.lib opengl32.lib comctl32.lib wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib imm32.lib "..\lib\fltk.lib" "..\lib\fltkgl.lib" foo.obj

C:\fltk-1.1.6-new\test>foo
Post by adam
I want to run the entire thing without seeing the console window at all,
running the program in a box in my GUI. Most programming IDEs use this
when compiling and stuff.
If you have a /subsystem:windows app, you'd have to use
a combo of CreatePipe() and CreateProcess() to avoid the
DOS window, and threads to read the pipe, and shove the results
into FLTK's widget.

This is not for the meek; win32's API is really a PITA.
CreateProcess() is pretty darn ugly, and threads can be
tricky if not done correctly.

For specifics on using CreatePipe() and CreateProcess() and threads
in general, see my recent post on comp.os.ms-windows.programmer.win32:
http://groups.google.com/group/comp.os.ms-windows.programmer.win32/msg/0ee700332f211d71?dmode=source

..which shows a complete /WIN32/ example. No FLTK involved.
But the techniques with CreateProcess() and CreatePipe() and
threads would be the same.

It's up to you to take that code and modify it so that the
threads send data to the FLTK widget safely. Get as far as
you can, and follow up here if you really hit a wall.
Keep thread safety in mind.
Post by adam
The Popen example mentioned earlier uses fgets to read the stream,
however, that will only read parts of the stream. when i run the
console programs using popen, i get SOME text in the GUI window,
but some text is still sent to the console window.
That's probably stderr text you're getting in the console window.
popen() only reads stdout.

If you want popen() to grab /both/ stdout /and/ stderr
from the app, you'll need to invoke the app with 2>&1, eg:

popen("yourapp -arg1 -arg2 2>&1", "r");
Greg Ercolano
2007-06-02 07:31:12 UTC
Permalink
Post by Greg Ercolano
Post by adam
I'm looking for a function similar to the wxExecute function. http://www.wxwidgets.org/wiki/index.php/WxExecute this allows you to do command line stuff without using the command line.
I've tried using the popen example from Greg Ercolano, but its not working.
What problem are you having?
I just compiled it again on my Visual Studio 6 and it ran fine. [..]
BTW, I'm assuming you mean this example:
http://seriss.com/people/erco/fltk/#add_fd

..and /not/ this other one, which also uses popen, but is unix only:
http://seriss.com/people/erco/fltk/#mkfifo

Life will definitely be easier if you use popen(), because then
you don't have to use threads.. add_fd() does all the work for you.
add_fd() only works with the unixy 'file descriptors' returned by
calls like popen/fopen/open/etc. add_fd() /won't/ work with
CreatePipe()/CreateFile(), because Microsoft wanted to be different.
gga
2007-06-02 12:23:46 UTC
Permalink
Post by adam
I'm looking for a function similar to the wxExecute function. http://www.wxwidgets.org/wiki/index.php/WxExecute this allows you to do command line stuff without using the command line.
There's nothing specifically in FLTK to do what you want, as that's not
something that belongs to a GUI toolkit.

You need some platform specific code, which is ugly both on windows and
linux (and also troublesome to debug).

Find attached a simple example (taken from my mrLiquid) that simplifies
Windows (win32 - not tested under vista) and Linux cumbersome apis to a
simpler api. You will just need to change one or two stuff, like the
macro LOG_ERROR and the win32_error_to_MString() function.

For simple stuff, rely on using mr_system() which works just like
system().
If you need to control where input comes from and output/error messages
go or need to allow killing the process at some point, use the more
complex mr_popen3().
--
Gonzalo Garramuño
ggarra-ejQy8aU/***@public.gmane.org

AMD4400 - ASUS48N-E
GeForce7300GT
Kubuntu Edgy
gga
2007-06-02 12:26:33 UTC
Permalink
Post by adam
I'm looking for a function similar to the wxExecute function. http://www.wxwidgets.org/wiki/index.php/WxExecute this allows you to do command line stuff without using the command line.
There's nothing specifically in FLTK to do what you want, as that's not
something that belongs to a GUI toolkit.

You need some platform specific code, which is ugly both on windows and
linux (and also troublesome to debug).

Find attached a simple example (taken from my mrLiquid) that simplifies
Windows (win32 - not tested under vista) and Linux cumbersome apis to a
simpler api. You will just need to change one or two stuff, like the
macro LOG_ERROR and the win32_error_to_MString() function.

For simple stuff, rely on using mr_system() which works just like
system().
If you need to control where input comes from and output/error messages
go or need to allow killing the process at some point, use the more
complex mr_popen3().
--
Gonzalo Garramuño
ggarra-ejQy8aU/***@public.gmane.org

AMD4400 - ASUS48N-E
GeForce7300GT
Kubuntu Edgy
adam
2007-06-02 17:22:44 UTC
Permalink
Yes, I used the popen example.

I tried compiling the code u sent, and I'm getting errors. I use quincy2005 which uses the mingw compiler. errors are:
"
23: error 'vsprintf_s' was not declared i this scope
52: error 'sprintf_s' was not declared in this scope
"

the popen example works great. its only when i try to change it that I'm getting problems.

try this and see if it works on your machine:
download avra.exe (avr assembler) http://sourceforge.net/projects/avra

put that in the same folder as your .exe and call it from within the program using this:
"avra.exe test.asm"
when it tries to compile, it'll show the avra stuff in the GUI, however, the error will show in the console something like "test.asm: file does not exist"

Now that's if the file doesn't exist. but even if it does and there are no errors, avra outputs "pass 1..." in the GUI window, but everything below that shows up in the DOS window, "pass 2..." etc. If there are any errors in compilation, those will also show up in the console window.

That's just one program I'm calling. The other program I'm calling will show show nothing in the GUI window, but everything in the console. regardless of if its job was successful or not.

Thanks for your help, I REALLY appreciate it.

Adam
Post by Greg Ercolano
Post by Greg Ercolano
Post by adam
I'm looking for a function similar to the wxExecute function. http://www.wxwidgets.org/wiki/index.php/WxExecute this allows you to do command line stuff without using the command line.
I've tried using the popen example from Greg Ercolano, but its not working.
What problem are you having?
I just compiled it again on my Visual Studio 6 and it ran fine. [..]
http://seriss.com/people/erco/fltk/#add_fd
http://seriss.com/people/erco/fltk/#mkfifo
Life will definitely be easier if you use popen(), because then
you don't have to use threads.. add_fd() does all the work for you.
add_fd() only works with the unixy 'file descriptors' returned by
calls like popen/fopen/open/etc. add_fd() /won't/ work with
CreatePipe()/CreateFile(), because Microsoft wanted to be different.
Greg Ercolano
2007-06-02 17:43:29 UTC
Permalink
Post by adam
I tried compiling the code u sent, and I'm getting errors.
"
23: error 'vsprintf_s' was not declared i this scope
52: error 'sprintf_s' was not declared in this scope
"
If you mean the code on the google groups link, that's a program
intended for Microsoft's VS compiler.

If you're using something else, you might need to add some #include's
check your compiler's docs for those functions shown above, and see
what file needs to be #include'ed, then add those at the top.
Post by adam
That's just one program I'm calling. The other program I'm calling
will show show nothing in the GUI window, but everything in the console.
regardless of if its job was successful or not.
Are you sure you added the "2>&1" to your popen() command?
Post by adam
Post by Greg Ercolano
If you want popen() to grab /both/ stdout /and/ stderr
popen("yourapp -arg1 -arg2 2>&1", "r");
^^^^
adam
2007-06-02 17:28:03 UTC
Permalink
Sorry, I did not receive the attached example you mentioned. I'm using the web interface to this forum and it didn't show up. I know it'd be a hassle, but could you put it somewhere? I'm interested in using mrLiquid. I've never heard of it before, but I'm always up for trying something new when I run into problems.

Thanks,
Adam
This is a multi-part message in MIME format.
--Boundary_(ID_+ERfKm/GgQ5K64p43mZmtw)
Content-type: text/plain; charset=ISO-8859-1
Content-transfer-encoding: 8BIT
Post by adam
I'm looking for a function similar to the wxExecute function. http://www.wxwidgets.org/wiki/index.php/WxExecute this allows you to do command line stuff without using the command line.
There's nothing specifically in FLTK to do what you want, as that's not
something that belongs to a GUI toolkit.
You need some platform specific code, which is ugly both on windows and
linux (and also troublesome to debug).
Find attached a simple example (taken from my mrLiquid) that simplifies
Windows (win32 - not tested under vista) and Linux cumbersome apis to a
simpler api. You will just need to change one or two stuff, like the
macro LOG_ERROR and the win32_error_to_MString() function.
For simple stuff, rely on using mr_system() which works just like
system().
If you need to control where input comes from and output/error messages
go or need to allow killing the process at some point, use the more
complex mr_popen3().
--
Gonzalo Garramuï¿œo
AMD4400 - ASUS48N-E
GeForce7300GT
Kubuntu Edgy
--Boundary_(ID_+ERfKm/GgQ5K64p43mZmtw)--
gga
2007-06-03 07:44:01 UTC
Permalink
Post by adam
Sorry, I did not receive the attached example you mentioned. I'm using the web interface to this forum and it didn't show up. I know it'd be a hassle, but could you put it somewhere? I'm interested in using mrLiquid. I've never heard of it before, but I'm always up for trying something new when I run into problems.
Thanks,
Adam
Here it is again. This was tested with Linux and MSVC.
--
Gonzalo Garramuño
ggarra-ejQy8aU/***@public.gmane.org

AMD4400 - ASUS48N-E
GeForce7300GT
Kubuntu Edgy
Jonathan Murphy
2007-06-03 09:28:54 UTC
Permalink
I'm on the mailing list (for some reason I can't access the newsgroup
with my ISP), and I see no attachment. Do attachments get scrubbed for
the mirror?
Post by gga
Post by adam
Sorry, I did not receive the attached example you mentioned. I'm
using the web interface to this forum and it didn't show up. I know
it'd be a hassle, but could you put it somewhere? I'm interested in
using mrLiquid. I've never heard of it before, but I'm always up for
trying something new when I run into problems.
Thanks, Adam
Here it is again. This was tested with Linux and MSVC.
--
Gonzalo Garramuño
AMD4400 - ASUS48N-E
GeForce7300GT
Kubuntu Edgy
_______________________________________________
fltk mailing list
http://lists.easysw.com/mailman/listinfo/fltk
gga
2007-06-03 09:41:20 UTC
Permalink
Post by Jonathan Murphy
I'm on the mailing list (for some reason I can't access the newsgroup
with my ISP), and I see no attachment. Do attachments get scrubbed for
the mirror?
No, it seems they get killed. The second message was intended for him
privately, but Greg already helped him out.
--
Gonzalo Garramuño
ggarra-ejQy8aU/***@public.gmane.org

AMD4400 - ASUS48N-E
GeForce7300GT
Kubuntu Edgy
adam
2007-06-02 18:06:48 UTC
Permalink
You were right, I missed that. It's working much better now thank you!

I only have one small problem left, but I'm sure I'll figure that out. if the file doesn't exist, the line saying "file does not exist" doesn't show up on either screen, but that can be remedied and is likely not going to be possible in my program. Thanks again for your help!


Adam
Post by Greg Ercolano
Post by adam
I tried compiling the code u sent, and I'm getting errors.
"
23: error 'vsprintf_s' was not declared i this scope
52: error 'sprintf_s' was not declared in this scope
"
If you mean the code on the google groups link, that's a program
intended for Microsoft's VS compiler.
If you're using something else, you might need to add some #include's
check your compiler's docs for those functions shown above, and see
what file needs to be #include'ed, then add those at the top.
Post by adam
That's just one program I'm calling. The other program I'm calling
will show show nothing in the GUI window, but everything in the console.
regardless of if its job was successful or not.
Are you sure you added the "2>&1" to your popen() command?
Post by adam
Post by Greg Ercolano
If you want popen() to grab /both/ stdout /and/ stderr
popen("yourapp -arg1 -arg2 2>&1", "r");
^^^^
adam
2007-06-02 19:06:07 UTC
Permalink
Ok, sorry about this, but how can I use this in a callback?

I have tried 2 things:
1) trying to make this part of the same window as the text editor,
2)just calling it to open the popen window from a callback

neither of these work correctly. I can open the peopen window as a second window(not really what i want to do, but its at least something)but i must run whatever command through there as the window opens.

The Fl::add_fd(fileno(G_fp), HandleFD, (void*)&brow); command is really giving me problems too if I try to open the window from a callback.

I would like this to be in the same window as my text editor, however, i can't get that to work at all. (using the text editor example given with Quincy2005) If you can help me do that instead of opening it in a new window, I would really appreciate it.


Thanks again for all your help,
Adam
Post by adam
You were right, I missed that. It's working much better now thank you!
I only have one small problem left, but I'm sure I'll figure that out. if the file doesn't exist, the line saying "file does not exist" doesn't show up on either screen, but that can be remedied and is likely not going to be possible in my program. Thanks again for your help!
Adam
Post by Greg Ercolano
Post by adam
I tried compiling the code u sent, and I'm getting errors.
"
23: error 'vsprintf_s' was not declared i this scope
52: error 'sprintf_s' was not declared in this scope
"
If you mean the code on the google groups link, that's a program
intended for Microsoft's VS compiler.
If you're using something else, you might need to add some #include's
check your compiler's docs for those functions shown above, and see
what file needs to be #include'ed, then add those at the top.
Post by adam
That's just one program I'm calling. The other program I'm calling
will show show nothing in the GUI window, but everything in the console.
regardless of if its job was successful or not.
Are you sure you added the "2>&1" to your popen() command?
Post by adam
Post by Greg Ercolano
If you want popen() to grab /both/ stdout /and/ stderr
popen("yourapp -arg1 -arg2 2>&1", "r");
^^^^
Greg Ercolano
2007-06-02 22:02:58 UTC
Permalink
Post by adam
Ok, sorry about this, but how can I use this in a callback?
1) trying to make this part of the same window as the text editor,
2)just calling it to open the popen window from a callback
neither of these work correctly.
Hard to tell the problem just by the description,
as the devil's in the details.

Paste a small compilable example program here, similar
to my examples, showing what you're trying.

Remove all extraneous stuff, and just keep it simple.
Have your popen() invoke a child program that is an
OS command (like 'netstat -an') so that a separate
app isn't needed.
adam
2007-06-03 00:46:01 UTC
Permalink
ok, below is the code. That's about as small as I can make it from what I know. Again, I'm a beginner.

I want it to work like this:
Selecting something from the menu will construct a command for popen. I haven't implemented that in this code because I don't know how to yet.
Once the command is made in the callback, it is stored in a string. when I want to actually run that command, I'll select "run command" from the menu. All of the popen stuff happens in the area labled "Console Output" in the main window. I'd like it all to be within one window.


Here are a few problems I've been having with this kind of setup:

1) I can't do the "Fl::add_fd(fileno(G_fp), HandleFD, (void*)&brow);" command from anywhere except where the browser is initialized.

2) I can't run the window form a callback because I can't have "return Fl::run();" in there of course

3)I can't open a browser inside the text editor window (section in the code on line 111)

4)I can't change the command sent to popen.

5) the popen command must be there before the "Fl::add_fd(fileno(G_fp), HandleFD, (void*)&brow);" command of course.


The code below shows the only way I've gotten it to successfully run so far. Believe me, today is not the only time I've worked on this.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#ifdef __MWERKS__
# define FL_DLL
#endif
#include <FL/Fl.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_File_Chooser.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Text_Buffer.H>
#include <FL/Fl_Text_Editor.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Multi_Browser.H>

int changed = 0;
char filename[256] = "";
char title[256];
Fl_Text_Buffer *textbuf = 0;

#ifdef _WIN32
#include <windows.h>
#define popen _popen
#define pclose _pclose
#else
#include <unistd.h>
#endif

FILE *G_fp = NULL;

void HandleFD(int fd, void *data);

void
style_unfinished_cb(int, void*) {
}

class EditorWindow : public Fl_Double_Window {
public:
EditorWindow(int w, int h, const char* t);
~EditorWindow();
Fl_Text_Editor *editor;
char search[256];
};

EditorWindow::EditorWindow(int w, int h, const char* t) : Fl_Double_Window(w, h, t) {
}

EditorWindow::~EditorWindow() {
}

int loading = 0;
void load_file(char *newfile, int ipos) {
loading = 1;
int insert = (ipos != -1);
changed = insert;
if (!insert) strcpy(filename, "");
int r;
if (!insert) r = textbuf->loadfile(newfile);
else r = textbuf->insertfile(newfile, ipos);
if (r)
fl_alert("Error reading from file \'%s\':\n%s.", newfile, strerror(errno));
else
if (!insert) strcpy(filename, newfile);
loading = 0;
textbuf->call_modify_callbacks();
}

int num_windows = 0;

void do_cb()
{
//This callback constructs a string that is to be sent to popen
}

void do_cb2()
{
//This callback constructs another string that is to be sent to popen
}
void run_cb()
{
/*This is where I'd like to run the command from the above callbacks using popen,
but the "Fl::add_fd(fileno(G_fp), HandleFD, (void*)&brow);" won't run here.*/
}

Fl_Window* new_view();

void view_cb(Fl_Widget*, void*) {
Fl_Window* w = new_view();
w->show();
}

Fl_Menu_Item menuitems[] = {
{ "&do", 0, 0, 0, FL_SUBMENU },
{ "do", FL_CTRL + 's', (Fl_Callback *)do_cb },
{ "do2", FL_CTRL + 'a', (Fl_Callback *)do_cb2 },
{ "Run command", FL_CTRL + 'r', (Fl_Callback *)run_cb },
{ 0 },
{ 0 }
};

Fl_Window* new_view() {
EditorWindow* w = new EditorWindow(660, 600, title);
w->begin();
Fl_Menu_Bar* m = new Fl_Menu_Bar(0, 0, 660, 30);
m->copy(menuitems, w);
w->editor = new Fl_Text_Editor(0, 400, 655, 197,"Console Output"); /*<-- I'd like the popen stuff to run in this area,
tho popen stuff should only be run after
the command is made in one of the callbacks
such as "do_cb" and "do_cb2"*/
w->editor = new Fl_Text_Editor(0, 30, 655, 350);
w->editor->buffer(textbuf);
w->editor->textfont(FL_COURIER);
w->end();
w->resizable(w->editor);
textbuf->call_modify_callbacks();
num_windows++;
return w;
}


void HandleFD(int fd, void *data) {
Fl_Multi_Browser *brow = (Fl_Multi_Browser*)data;
char s[1024];
if ( fgets(s, 1023, G_fp) == NULL ) {
Fl::remove_fd(fileno(G_fp));
pclose(G_fp);
return;
}
brow->add(s);
}

int main(int argc, char **argv) {
textbuf = new Fl_Text_Buffer;
Fl_Window* window = new_view();
window->show(1, argv);
if (argc > 1) load_file(argv[1], -1);

// this is the Popen window stuff:
Fl_Window win(600,600);
Fl_Multi_Browser brow(10,10,580,580);
G_fp = popen("netstat -an 2>&1","r");
Fl::add_fd(fileno(G_fp), HandleFD, (void*)&brow); /*<- This is the root to one of my main problems.
it MUST have somethign in G_fp and can't be called
from the callback since "brow" isn't defined there*/
win.resizable(brow);
win.show();
return Fl::run();
}
Greg Ercolano
2007-06-03 01:44:09 UTC
Permalink
Post by adam
1) I can't do the "Fl::add_fd(fileno(G_fp), HandleFD, (void*)&brow);" command
from anywhere except where the browser is initialized.
Why not; what is preventing you?

If it's because you don't have access to 'brow' because of the
variable's scope, either make it a global, or pass it as userdata
to the callback from main().
Post by adam
2) I can't run the window [from] a callback because I can't have
"return Fl::run();" in there of course
"Run the window"?
Not sure what you mean, can you clarify?

When a callback is invoked, you do what you need to do
in that function, creating/manipulating the widgets, whatever.
When you return from the callback, the Fl::run() loop will
regain control.

If you're trying to access the window from the callback and can't
because of the window's variable scope, change the scope by either
making it a global, or passing a pointer to it as userdata to the
callback.
Post by adam
3)I can't open a browser inside the text editor window (section in the code on line 111)
I think if I show you a clean example, you'll get a better idea
of how it works.
Post by adam
4)I can't change the command sent to popen.
What prevents you?
Make it a global and then you can change it from anywhere in
the app, and your popen() command can access it.
Post by adam
5) the popen command must be there before the "Fl::add_fd(fileno(G_fp), HandleFD, (void*)&brow);"
command of course.
My guess is you've got a problem with variable scope, and just need to make
brow a global, so that you can access it from wherever you end up putting
your popen() command.

I'll try to follow up with a rewrite of your app, so that you can get
an idea of how it should be laid out. Shouldn't be hard.
Greg Ercolano
2007-06-03 02:38:33 UTC
Permalink
Post by Greg Ercolano
I'll try to follow up with a rewrite of your app, so that you can get
an idea of how it should be laid out. Shouldn't be hard.
I rewrote it -- you may not recognize it, but this is more likely
how it should be laid out.. this approach will 'scale' to larger apps.

Notice how everything is in the EditorWindow class now.. this
way all the methods/callbacks can access all the data it needs
without it being global.

This means you can easily create several instances of EditorWindow,
each with its own commands that can be running concurrently.
Post by Greg Ercolano
I wasn't sure what the multibrowser was for, so I took it out.
I also removed the file load stuff, because it seemed unrelated
to the problem. Note that Fl_Text_Buffer has a loadfile() method,
so you can use that to load the editor with a text file, rather
than writing it yourself, though maybe you needed something custom,
I didn't look.
Post by Greg Ercolano
I rewrote the menu code a bit.. I dislike using that static menu
array stuff, and prefer using the menubar->add() approach.

Study the design of this, and see if you can see why it has
been laid out this way. Putting all the variables into the class
cleans up all the variable scoping, so that everything has access
to what it needs, cleanly, without all those nasty globals.

Follow up with questions if you need to, but I think if you look
at it carefully, you'll get it.

The nasty globals in the original demo app were just to keep
the demo simple. It's assumed the reader is familiar with the
mechanics of C++, and after reading the example, can re-scope the
the variables as they please.

HTH.

---- snip

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#ifdef _WIN32
#include <windows.h>
#define popen _popen
#define pclose _pclose
#else
#include <unistd.h>
#endif
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Text_Buffer.H>
#include <FL/Fl_Text_Editor.H>
#include <FL/Fl_Window.H>

class EditorWindow : public Fl_Double_Window {
// Menubar
Fl_Menu_Bar *menubar;

// File editor
Fl_Text_Editor *file_edit;
Fl_Text_Buffer *file_buff;

// Console editor
Fl_Text_Editor *console_edit;
Fl_Text_Buffer *console_buff;

// Popen stuff
FILE *fp;
char command[512];

// DATA CALLBACK: Handle reading data from running command
void HandleFD() {
static char s[1024];
if ( fgets(s, sizeof(s)-1, fp) == NULL ) {
Fl::remove_fd(fileno(fp));
pclose(fp);
fp = 0;
console_buff->append("--- COMMAND COMPLETED\n");
} else {
console_buff->append(s);
}
}
static void HandleFD_CB(int, void *userdata) {
EditorWindow *ewin = (EditorWindow*)userdata;
ewin->HandleFD(); // avoids having to do ewin->xxx to access vars
}
// MENU CALLBACK: Build command
static void MakeCommand_CB(Fl_Widget*, void*userdata) {
EditorWindow *ewin = (EditorWindow*)userdata;
strcpy(ewin->command, "netstat -an 2>&1"); // create the command
}
// MENU CALLBACK: Start command running
void RunCommand() {
if ( fp != 0 ) return; // command already running? ignore
console_buff->text(""); // clear window
if ( command[0] == 0 ) { // no command? complain
console_buff->text("No command specified");
return;
}
fp = popen(command, "r"); // start command running..
if ( fp == NULL ) { // Failed? show error
console_buff->append("ERROR: popen: ");
console_buff->append(strerror(errno));
} else { // Worked? setup handler
Fl::add_fd(fileno(fp), HandleFD_CB, (void*)this);
}
}
// MENU CALLBACK: Start command running
static void RunCommand_CB(Fl_Widget*, void*userdata) {
EditorWindow *ewin = (EditorWindow*)userdata;
ewin->RunCommand(); // avoids having to do ewin->xxx to access vars
}

public:
// Ctor
EditorWindow(int w, int h, const char* t) : Fl_Double_Window(w, h, t) {
// Create menu bar
menubar = new Fl_Menu_Bar(0, 0, w, 25);
menubar->add("&File/Make Command", FL_CTRL+'s', MakeCommand_CB, (void*)this);
menubar->add("&File/Run", FL_CTRL+'r', RunCommand_CB, (void*)this);

// Create file editor
file_edit = new Fl_Text_Editor(0, 40, w, 260, "File Editor");
file_buff = new Fl_Text_Buffer();
file_edit->buffer(file_buff);
file_edit->textfont(FL_COURIER);

// Create console editor
console_edit = new Fl_Text_Editor(0, 320, w, 260, "Console Output");
console_buff = new Fl_Text_Buffer();
console_edit->buffer(console_buff);
console_edit->textfont(FL_COURIER);

// Popen stuff
fp = NULL;
command[0] = 0;

end();
resizable(this);
}
~EditorWindow() {
}
};

int main(int argc, char **argv) {
EditorWindow *ewin = new EditorWindow(600, 600, "Test");
ewin->show();
return(Fl::run());
}
Greg Ercolano
2007-06-03 02:42:05 UTC
Permalink
Post by Greg Ercolano
// DATA CALLBACK: Handle reading data from running command
void HandleFD() {
static char s[1024];
BTW, if you intend to make multiple concurrent instances of EditorWindow,
you should replace that static char s[] with just char s[].

I made it static cause I just didn't want it to keep going in and
out of scope on every line of data.

Also, you can improve the speed of how it loads the data by using
fread() instead of fgets(), so that it can load in blocks of text
at a time, instead of line-at-a-time, which is relatively slow.
adam
2007-06-03 07:12:26 UTC
Permalink
Wow, your code is MUCH cleaner than mine. I basically did a hack job on the rest of the program just to get things to work. Now I'm going to redo my entire program based off the framework you showed me. That way I can keep things tight without all the mess I had before. It's going to take me a while, but in the end it'll be worth it. As I said earlier I am still learning this stuff.

I see what you did and the way things are laid out is almost perfect for my program. With a little tweaking here and there, I should be able to modify most of my other program to do what I need. There are just a bunch of different ways to do things like this. With some studying on this program and FLUID code I'll make, I think I'll get things down before too long.

On a side note, do you plan on making any more FLTK videos? They are a HUGE help and I'd love to get into some class stuff (that's the thing I feel I'm lacking most in C++)

Thanks for all your help,
Adam
Post by Greg Ercolano
Post by Greg Ercolano
I'll try to follow up with a rewrite of your app, so that you can get
an idea of how it should be laid out. Shouldn't be hard.
I rewrote it -- you may not recognize it, but this is more likely
how it should be laid out.. this approach will 'scale' to larger apps.
Notice how everything is in the EditorWindow class now.. this
way all the methods/callbacks can access all the data it needs
without it being global.
This means you can easily create several instances of EditorWindow,
each with its own commands that can be running concurrently.
Post by Greg Ercolano
I wasn't sure what the multibrowser was for, so I took it out.
I also removed the file load stuff, because it seemed unrelated
to the problem. Note that Fl_Text_Buffer has a loadfile() method,
so you can use that to load the editor with a text file, rather
than writing it yourself, though maybe you needed something custom,
I didn't look.
Post by Greg Ercolano
I rewrote the menu code a bit.. I dislike using that static menu
array stuff, and prefer using the menubar->add() approach.
Study the design of this, and see if you can see why it has
been laid out this way. Putting all the variables into the class
cleans up all the variable scoping, so that everything has access
to what it needs, cleanly, without all those nasty globals.
Follow up with questions if you need to, but I think if you look
at it carefully, you'll get it.
The nasty globals in the original demo app were just to keep
the demo simple. It's assumed the reader is familiar with the
mechanics of C++, and after reading the example, can re-scope the
the variables as they please.
HTH.
---- snip
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#ifdef _WIN32
#include <windows.h>
#define popen _popen
#define pclose _pclose
#else
#include <unistd.h>
#endif
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Text_Buffer.H>
#include <FL/Fl_Text_Editor.H>
#include <FL/Fl_Window.H>
class EditorWindow : public Fl_Double_Window {
// Menubar
Fl_Menu_Bar *menubar;
// File editor
Fl_Text_Editor *file_edit;
Fl_Text_Buffer *file_buff;
// Console editor
Fl_Text_Editor *console_edit;
Fl_Text_Buffer *console_buff;
// Popen stuff
FILE *fp;
char command[512];
// DATA CALLBACK: Handle reading data from running command
void HandleFD() {
static char s[1024];
if ( fgets(s, sizeof(s)-1, fp) == NULL ) {
Fl::remove_fd(fileno(fp));
pclose(fp);
fp = 0;
console_buff->append("--- COMMAND COMPLETED\n");
} else {
console_buff->append(s);
}
}
static void HandleFD_CB(int, void *userdata) {
EditorWindow *ewin = (EditorWindow*)userdata;
ewin->HandleFD(); // avoids having to do ewin->xxx to access vars
}
// MENU CALLBACK: Build command
static void MakeCommand_CB(Fl_Widget*, void*userdata) {
EditorWindow *ewin = (EditorWindow*)userdata;
strcpy(ewin->command, "netstat -an 2>&1"); // create the command
}
// MENU CALLBACK: Start command running
void RunCommand() {
if ( fp != 0 ) return; // command already running? ignore
console_buff->text(""); // clear window
if ( command[0] == 0 ) { // no command? complain
console_buff->text("No command specified");
return;
}
fp = popen(command, "r"); // start command running..
if ( fp == NULL ) { // Failed? show error
console_buff->append("ERROR: popen: ");
console_buff->append(strerror(errno));
} else { // Worked? setup handler
Fl::add_fd(fileno(fp), HandleFD_CB, (void*)this);
}
}
// MENU CALLBACK: Start command running
static void RunCommand_CB(Fl_Widget*, void*userdata) {
EditorWindow *ewin = (EditorWindow*)userdata;
ewin->RunCommand(); // avoids having to do ewin->xxx to access vars
}
// Ctor
EditorWindow(int w, int h, const char* t) : Fl_Double_Window(w, h, t) {
// Create menu bar
menubar = new Fl_Menu_Bar(0, 0, w, 25);
menubar->add("&File/Make Command", FL_CTRL+'s', MakeCommand_CB, (void*)this);
menubar->add("&File/Run", FL_CTRL+'r', RunCommand_CB, (void*)this);
// Create file editor
file_edit = new Fl_Text_Editor(0, 40, w, 260, "File Editor");
file_buff = new Fl_Text_Buffer();
file_edit->buffer(file_buff);
file_edit->textfont(FL_COURIER);
// Create console editor
console_edit = new Fl_Text_Editor(0, 320, w, 260, "Console Output");
console_buff = new Fl_Text_Buffer();
console_edit->buffer(console_buff);
console_edit->textfont(FL_COURIER);
// Popen stuff
fp = NULL;
command[0] = 0;
end();
resizable(this);
}
~EditorWindow() {
}
};
int main(int argc, char **argv) {
EditorWindow *ewin = new EditorWindow(600, 600, "Test");
ewin->show();
return(Fl::run());
}
Continue reading on narkive:
Loading...