Guification of console app

M

metiu

Say I have a console app that does something in three steps:
- opens a file
- transfers the file through a serial port
- does some elaborations

and I want to build a GUI around it that, for example, sets the file
name to open or starts the different steps.

I started using wxPython (actually boa-constructor) to build a frame
with everything I want.
The problem is: when the console app method is working, the GUI is
locked...

How can the console app communicate with the GUI in order to make it
tick (think of status messages, progress bars, log messages...)?
If I use threads and events, or wxYield, or EVT_IDLE, I'm tweaking the
console app so that it won't work just in console anymore.

How do you do that?

Thanks!
 
P

Peter Hansen

metiu said:
Say I have a console app that does something in three steps:
- opens a file
- transfers the file through a serial port
- does some elaborations

and I want to build a GUI around it that, for example, sets the file
name to open or starts the different steps.

I started using wxPython (actually boa-constructor) to build a frame
with everything I want.
The problem is: when the console app method is working, the GUI is
locked...

How can the console app communicate with the GUI in order to make it
tick (think of status messages, progress bars, log messages...)?
If I use threads and events, or wxYield, or EVT_IDLE, I'm tweaking the
console app so that it won't work just in console anymore.

Could you please explain what makes this a "console" app, specifically?
You are proposing wrapping a GUI around it, which would make it not a
console app (by definition), so it would help if you specified exactly
what elements (which functions, etc.) cause you to label it a "console"
app. (For example, maybe it calls msvcrt.kbhit/getch or something...)

The reason for asking is that the specifics of your answer will make it
easier to describe how to modify this "console" so that the relevant
parts work equally well as a _real_ console app (no GUI) or with the
GUI. We could describe it in more general terms, but it might not be
apparent how to adapt that to your own case.

-Peter
 
S

Steve Holden

metiu said:
Say I have a console app that does something in three steps:
- opens a file
- transfers the file through a serial port
- does some elaborations

and I want to build a GUI around it that, for example, sets the file
name to open or starts the different steps.

I started using wxPython (actually boa-constructor) to build a frame
with everything I want.
The problem is: when the console app method is working, the GUI is
locked...

How can the console app communicate with the GUI in order to make it
tick (think of status messages, progress bars, log messages...)?
If I use threads and events, or wxYield, or EVT_IDLE, I'm tweaking the
console app so that it won't work just in console anymore.

How do you do that?

Thanks!
I've achieved this in the past by having the GUI trigger the console app
in a separate thread and then use a Queue.Queue to send output from the
console app to the GUI. As long as the GUI portion is careful never to
block on an empty Queue this can work quite well.

There's also a cookbook recipe by Jacob Hallen that shows you how to
keep a Tkinter GUI running with parallel worker threads.

regards
Steve
 
M

metiu

Yes, I'll try to give you an example:
you have a compression utility that works as a standard *nix filter, so
it takes something from stdin and gives it back compressed to stdout
you like to use it as such, because it's nice to call it from the
command line

now someone finds your utility quite nice, and says it would be nice to
have a GUI that shows you, for example, how long it will take to
compress...

one way for sure it would be to fork your app, but this would be a
waste of time

you'd like to reuse the compression library you've already written for
your GUI and your console, but:
- you'd like to have your console app clean and simple, such as:

import sys
import CompressLib

data = sys.stdin.read()
cdata = CompressLib.compress(data)
print cdata

but you'd like the GUI to show some progress and status info.

I guess the way is still to make the compress call split in microsteps
and have the app call it, so you can use an EVT_IDLE call from the GUI,
but maybe there's a better way...

Thanks again



Peter Hansen ha scritto:
 
F

Fredrik Lundh

metiu said:
you have a compression utility that works as a standard *nix filter, so
it takes something from stdin and gives it back compressed to stdout
you like to use it as such, because it's nice to call it from the
command line

now someone finds your utility quite nice, and says it would be nice to
have a GUI that shows you, for example, how long it will take to
compress...

one way for sure it would be to fork your app, but this would be a
waste of time

you'd like to reuse the compression library you've already written for
your GUI and your console, but:
- you'd like to have your console app clean and simple, such as:

import sys
import CompressLib

data = sys.stdin.read()
cdata = CompressLib.compress(data)
print cdata

but you'd like the GUI to show some progress and status info.

here's a simple, stupid, and portable solution. the first script simulates
your compression utility:

# compress.py (simulator)

import time, sys

for i in range(100):
sys.stdout.write(".")
sys.stderr.write("%d%% done\r" % i)
sys.stderr.flush()
time.sleep(0.1)

(it prints "N% done" messages to stderr during the compression)

the second script simulates your GUI. the stuff in the while loop should
be run by a timer/alarm function, at regular intervals:

import re, subprocess, time, os

class monitor:
# looks for "N% done" messages in the output stream
def __init__(self, tfile):
# could use dup/reopen instead
self.file = open(tfile.name, "r")
def poll(self):
pos = self.file.tell()
data = self.file.read()
if data:
data = re.findall("(\d+)% done", data)
if not data:
self.file.seek(pos)
else:
return int(data[-1])
def close(self):
self.file.close()

ifile = open("in.txt", "rb")
ofile = open("out.dat", "wb")
tfile = open("out.tmp~", "wb")

p = subprocess.Popen(
"python compress.py",
stdin=ifile, stdout=ofile, stderr=tfile,
)

m = monitor(tfile)

while 1:
# this should be placed in a background task that's called
# every second or so
if p.poll() is not None:
print "DONE"
break
status = m.poll()
if status is not None:
# update status monitor
print status, "PERCENT DONE"
# wait a while before calling the background task again
print "."
time.sleep(0.5)

# clean up
ifile.close()
ofile.close()
m.close()
name = tfile.name
tfile.close()
os.remove(name)

if you limit yourself to Unix only, you can simplify things quite a bit (e.g.
using a pipe instead of the temporary file and use select to poll it, or use
dup/reopen tricks to avoid opening the temporary file twice; if you do
the latter, you can also use safe tempfile creation methods (see the
"tempfile" method for details. etc).

hope this helps!

</F>
 
C

Chris Mellon

metiu said:
you have a compression utility that works as a standard *nix filter, so
it takes something from stdin and gives it back compressed to stdout
you like to use it as such, because it's nice to call it from the
command line

now someone finds your utility quite nice, and says it would be nice to
have a GUI that shows you, for example, how long it will take to
compress...

one way for sure it would be to fork your app, but this would be a
waste of time

you'd like to reuse the compression library you've already written for
your GUI and your console, but:
- you'd like to have your console app clean and simple, such as:

import sys
import CompressLib

data = sys.stdin.read()
cdata = CompressLib.compress(data)
print cdata

but you'd like the GUI to show some progress and status info.

here's a simple, stupid, and portable solution. the first script simulates
your compression utility:

# compress.py (simulator)

import time, sys

for i in range(100):
sys.stdout.write(".")
sys.stderr.write("%d%% done\r" % i)
sys.stderr.flush()
time.sleep(0.1)

(it prints "N% done" messages to stderr during the compression)

the second script simulates your GUI. the stuff in the while loop should
be run by a timer/alarm function, at regular intervals:

import re, subprocess, time, os

class monitor:
# looks for "N% done" messages in the output stream
def __init__(self, tfile):
# could use dup/reopen instead
self.file = open(tfile.name, "r")
def poll(self):
pos = self.file.tell()
data = self.file.read()
if data:
data = re.findall("(\d+)% done", data)
if not data:
self.file.seek(pos)
else:
return int(data[-1])
def close(self):
self.file.close()

ifile = open("in.txt", "rb")
ofile = open("out.dat", "wb")
tfile = open("out.tmp~", "wb")

p = subprocess.Popen(
"python compress.py",
stdin=ifile, stdout=ofile, stderr=tfile,
)

m = monitor(tfile)

while 1:
# this should be placed in a background task that's called
# every second or so
if p.poll() is not None:
print "DONE"
break
status = m.poll()
if status is not None:
# update status monitor
print status, "PERCENT DONE"
# wait a while before calling the background task again
print "."
time.sleep(0.5)

# clean up
ifile.close()
ofile.close()
m.close()
name = tfile.name
tfile.close()
os.remove(name)

if you limit yourself to Unix only, you can simplify things quite a bit (e.g.
using a pipe instead of the temporary file and use select to poll it, or use
dup/reopen tricks to avoid opening the temporary file twice; if you do
the latter, you can also use safe tempfile creation methods (see the
"tempfile" method for details. etc).

hope this helps!

</F>

If you are using wxPython, you can skip writing this scaffolding
yourself and use wx.Execute and wx.Process, which will allow you to
execute other processes and re-direct thier input and output. The
wxPython demo has an example.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,218
Latest member
JolieDenha

Latest Threads

Top