I’m working on a Python project that is executed on a terminal (or console) for which I am planning to implement a GUI.
I did not major in CS so I really have no idea how to effectively design a terminal GUI such that:
- the user interface looks good
- in GUI, it is directed to a certain widget, let’s say, a text label, or a bottom bar, or a hide-able frame.
Do you have any suggestions?
Currently, I am using the print
function to provide essential information on STDOUT
during execution, so a lot of print
calls are distributed here and there in the code.
I’m thinking of using macro-like variables such as ‘FILE_NOT_EXISTS_MESSAGE’ for printing, and all of them and their values would be defined in one file.
Is that a standard way to do this?
Should I introduce a logging system?
In summary, I’m looking for a pattern for handling console output that is effective and adaptable.
2
I think it depends on how reusable you want your code to be. At its simplest you could just replace all of the print statements you have with a function of your own that works out what to do. You could implement a logging system with different log levels, but I’m sure there already is one.
One of the useful things about wrapping print is that you can also turn certain messages off if you want to.
Not sure if this matters to you but the semantics of print differ between the newest Python and what we had before, so writing your own wrapper could make the transition much easier.
May I suggest that you have a look at a Model / View / Controller style architecture.
That way you can write a Model and Controller implementation that does what your application needs to do, and then you can write two separate views. A GUI view and a console view.
Have a look at the curses package for python as a way of doing a GUI interface on a console app (if its appropriate), or you could use a command line style interface to define a defined script of actions on the data if the cmd tool is designed to be non interactive in its use.
Leaving logging scattered throughout your code and trying to do the right thing in each instance, is probably impractical.
There are a couple of popular approaches, differing mostly in how structured the internal/intermediate data is.
-
The Model-View-Controller scheme would split your application into roughly these components:
- Model is your internal application state and logic: it shouldn’t print anything (except perhaps fatal errors, to stderr), but should be able to send events by calling methods on object
- View would be an abstract base class in a statically-typed language: in Python you don’t need it at all, except as an idea of the interface called by the Model and implemented by:
- TerminalView, which will print something suitable to the terminal when Model calls a method
- GraphicalView, which will display the information graphically instead
A possible problem with this approach is that you have to come up with some format for the data passed from the Model to the View object, and it’s impossible for me to say how complex this is for your application, how flexible it needs to be, etc.
-
The porcelain/chrome scheme, as used by git:
this keeps the idea of a back-end (Model) that avoids thinking about how to present information, but it’s output is machine-parseable text.
You may still want a front-end to turn that machine-readable text output back into human-readable text output (as well as the new GUI front-end to display it graphically), in which case these are exactly equivalent to the two concrete View types described previously.
If you can format text output that’s human-readable and easily machine-parseable, so much the better: you can skip the terminal front-end and just make the GUI a wrapper around the existing terminal application.
Note that I’ve omitted the Controller part of MVC entirely, because it isn’t clear whether you actually need interaction and feedback, or just pretty output.
You should split UI and the logic, that’s for sure.
There are more pattern for this, one of them is MVC which @Ptolemy already mentioned. There are more of them. MVC may look too hard to grasp (a bit jokingly, no one really knows how to do MVC right; but that’s mainly because it has lots of distinct flavours during its long history), but even if you do some more naive system at the beginning, it is fine.
The goal you should have, is to split, as much as possible, the process of showing the data from the logic of the application. If you do not want to study MVC and all its variants (MVP, MVVM), you may do the simple blackboard and let both model (the logic part) as well as view (the printing part) access only the blackboard (blackboard is data abstraction where data are put into a store (“blackboard”) and more pieces of software (“agents”) are observing it for changes, each agent interested only in its own subset, and then take the data from the blackboard, do some action and put the result back). Blackboard-type of data is good to keep different parts of SW separated but still give them controlled way to communicate.
But, there are many ways to achieve the split.