Building GUI applications
Dr_Acula
Posts: 5,484
I would be grateful for some advice from C++ programmers regarding building a GUI development framework.
On the touchscreen we have standard sorts of objects - picture boxes, text boxes, radio boxes etc. These are currently coded in Spin and need to be ported over to C.
At the same time, it would be helpful to be able to develop graphical software on a PC.
So here is the design challenge - what graphical language could be used that could run on both a PC and a Propeller?
I have been playing around with vb.net and C#. Normally when you write code in those languages, you start off with a blank form and you drag and drop buttons and text boxes onto the form, and when it all looks good you double click those and write in the code. That is fine but the code ends up in two pieces - a file with all the code, and another file with a description of where all the textboxes etc actually are.
That is ok for a PC application where you never look at that second file, but not so useful for porting over to a microcontroller.. One solution is to add all the graphical objects in code. Then the code becomes one file instead of two.
So - here is some test code that puts a textbox on the screen, defines an array, puts Hello World in the array and then copies Hello to the textbox. That tests out a few things and it runs fine on a PC.
vb.net code
Public Class Form1 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim TextBox1 As New TextBox Me.Controls.Add(textbox1) TextBox1.Location = New Point(100, 100) TextBox1.Width = 75 Dim StringArray(100) As String StringArray(1) = "Hello World" TextBox1.Text = Strings.Left(StringArray(1), 5) End Sub End Class
We can convert that to C# with an online converter http://www.developerfusion.com/tools/convert/vb-to-csharp/, or write it in C# in the first place
using Microsoft.VisualBasic; using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; public class Form1 { private void Form1_Load(System.Object sender, System.EventArgs e) { TextBox TextBox1 = new TextBox(); this.Controls.Add(TextBox1); TextBox1.Location = new Point(100, 100); TextBox1.Width = 75; string[] StringArray = new string[101]; StringArray[1] = "Hello World"; TextBox1.Text = Strings.Left(StringArray[1], 5); } public Form1() { Load += Form1_Load; } }
Next little challenge - convert to C++. Well there are converters that can do that too. Either vb.net to C++, or C# to c++, and lots of other languages too. I used this one http://tangiblesoftwaresolutions.com/Demo.htm
//.h file code: #include <string> class Form1 { private: void Form1_Load(object *sender, System::EventArgs *e); public: Form1() { //VB TO C++ CONVERTER NOTE: Converted event handler wireups: __super::Load += new System::EventHandler(this, &Form1::Form1_Load); } }; //.cpp file code: void Form1::Form1_Load(object *sender, System::EventArgs *e) { TextBox *TextBox1 = new TextBox(); this->Controls->Add(TextBox1); TextBox1->Location = new Point(100, 100); TextBox1->Width = 75; std::string StringArray[101]; StringArray[1] = "Hello World"; TextBox1->Text = StringArray[1].substr(0, 5); }
Now that isn't going to run quite yet on GCC but it is getting closer. What it needs is the lower level code that does things like draw a textbox and load in a font for the text. All that is working in Spin however - it just needs porting over to C. And it will probably need a preconversion program. But maybe it can be done with #includes?
eg
TextBox *TextBox1 = new TextBox();
That will need to run a routine equivalent in Spin to a Start - and for a textbox, it will need to load in a font such as Arial 16. (other fonts can be used with Textbox1.font but there needs to be a default font).
The Textbox1.location goes and draws the textbox at a specific location which in spin is something like
PUB TextBox(x1,y1,x2,y2) ' draw the outline of the box ClearRectangle(x1,y1,x2,y2,RGBtoWord(255,255,255)) Drawline(x1-1,y1-1,x2,y1-1,RGBtoWord(64,64,64)) ' gray outline lines - extract values from paint shop pro Drawline(x1-2,y1-2,x2+1,y1-2,RGBtoWord(128,128,128)) ' horizontal upper light gray Drawline(x1-1,y1-1,x1-1,y2,RGBtoWord(64,64,64)) ' vertical left gray Drawline(x1-2,y1-2,x1-2,y2+1,RGBtoWord(128,128,128)) ' vertical left light gray Drawline(x2+2,y1-2,x2+2,y2+2,RGBtoWord(255,255,255)) ' vertical right white Drawline(x1-2,y2+2,x2+2,y2+2,RGBtoWord(255,255,255)) ' horizontal lower white
So I think what we need is
1) a generic Textbox class and
2) the code that associates Textbox1 with that Textbox class ie the "New" in this TextBox *TextBox1 = new TextBox();
The methods in that class can be copied from some or all of the methods in .net for a textbox, eg size, width, multiline, font.
So let's start really simple. Would it be possible to write a Textbox class (ie code) that is purely text based, no graphics, and when you invoke the method ->Text eg like in this line
TextBox1->Text = StringArray[1].substr(0, 5);
It goes off and sends that string up the serial port to the debug terminal?
In C++, would you build such a class as a .h include file or is it done some other way?
Comments
There is a dichotomy at work here, the Propeller is very memory constrained, but it has quite a bit of horsepower.
If I were to go a GUI framework all over again, I might look towards *OLD* microsoft windows as a potential model. I also have some older C/C++ books that discuss much of this and give source code away too. I'll dig up one of the more complete DOS based ones and give you some details.
But, from a "clean sheet" approach, knowing the props limitations, I might start like this:
Develop a drawing primitives interface first
This would be the clear screen, draw rectangle, draw pixel, draw line, etc. These are the "GDI" in windows parlance. Make certain that you abstract this enough that you can draw to VGA or LCD. Typically this means having a HAL layer that exposes a rudimentary interface to the hardware, smashing things that don't fit (colors on a b&w screen, dither this or threshold)
Next you need an event framework, this needn't be too complicated at first. Qt has a relatively simple event framework, and Qtopia was used in several PDA devices. At the least, you need to register and dispatch events. When someone touches the screen, the touch needs to be dispatched to the handler that determines which object clipping boundary that touch was in, then dispatch the event to that object. The object then decides what should happen from that input. Perhaps it's an OK button, then it need to emit an event that gets pushed onto the event bus.
You could shortcut the "clipping check" by just having an event bus, every event gets posted to the "bus". Let's say you have 20 event types on the bus. When a widget/object registers to listen for an event, it can specify if it should append the handler at the end of the list, or insert at the beginning. This way priority can be designated. Object that want to exert more or broader control will insert at the beginning, by default you append to the end. An object can decide if the event should chain to the next handler, or if it "consumes" the event and prevents it from passing on. In practical use it would make sense to "consume" the event to prevent a bunch of handlers that don't need to be run, from receiving the event, especially if it was to close a window or something.
So, now you have an event framework. Next you need the actual widget framework. Qt does this reasonably well. Basically every UI element is a separate class and you create forms programmatically. SimpleIDE is written in C++ and Qt, so you can review the code to get a sense.
In general, basically you say: "create a window", then you can add formatting boxes like grids, columns, or rows, and each cell contains a UI element. If you don't use a formatting element to place a widget, it gets place with automatic flow. The advantage to this is that UI construction is much like HTML, it renders dependent to the context, instead of like Windows where every element is at a fixed pel location in the window. you get device independence, but more important to your cause, you can make dialogs that work on different screen resolutions and orientations.
I wrote a Qt app that was intended for a 320x240 pixel screen, but it was developed, and worked, on a 1600x1200 screen, just with much finer detail.
If you got to http://www.dainst.com/ and click on the software preview, you will see some of the custom Qt widgets I wrote for it.
So, to summarize:
You need a hardware rendering layer to setup the physical drawing device and take low level drawing primitives
Then you need a GDI toolkit to implement the high level drawing abstracts, such as rectangles, lines, circles, images, etc
Then you need an event framework that can register, generate, and dispatch events to UI elements
Then you need a UI library to handle the drawing of UI elements and implement the interaction logic and event handlers
I think you could put together a very basic C++ based environment in 5kloc or less, and have it quite extensible, reusable, and retargetable.
Let's say you have only 20 possible events that can be generated or hooked, something like "screen press down", "screen press up", "screen tap", or "key down", "key up", "key tap". Pick 20 of your most meaningul events, most will be user interfacing type events, with a few being system related events, like resize or minimize.
Next, you have a fixed array of event, the array is size N where N is the number of events. The array is initialized so each element (which is a pointer), points to a structure or class object. Structs are easy and use little memory. The structure is a simply singlely linked list, with the tail initialized to NULL to indicate no handler. You could have 1 global handler that gets initialized to all places, just as a function to call when nothing is registered, mostly as a debug hook. The structure should have a callback pointer to the registered handler.
Next, you have a header file with all your event types enumerated, the value of this is the array index.
Events are generated, then dispatched. Since you are dealing with a multi-threaded environment, it would make sense to have a small queue that can contain events, like 8 entries. This would contain the event ID (enumerated event designator), and a pointer to the event message payload. The payload is opaque to the event framework, the handler casts this to a known structure or variable type to make sense of it. Perhaps you would have a generic event_payload structure with an integral union for easy casting. You could reserve 4 longs in the structure for static values, they could be literal longs, pointers, or bytes, all handled by the union. Unions are awesome, they are declared like structures, but every member of a union actually occupies the same space. You can have a bunch of different data types all pointing to the same memory, but with unique names. It allows you to alias the same piece of memory to different types. This is handy for doing bit manipulation or accessing individual bytes of a long, etc.
So, something generates an event, when it does, it specifies the event ID and payload, that goes in the queue, if needed, then gets dispatched. The dispatcher pulls up the linked list at the array index pointed to by the event ID, then proceeds to execute the handlers in order. The return value of the handler should be a defined constant. The constants could be something like "abort", "ok", "halt". Abort is an exception, ok means continue the list, halt means the handler was successful, but it consumed the event and no further processing is required.
That's about all there is for the event framework, an array, register (head or tail), unregister, dispatch, and queuing.
When a window closes, it would emit a "window close" event, which would cause all of the objects in the current window to unregister their events. You could make a "background" handler that was a window that launched, but didn't unregister a handler, so it kept calling it. For a key logger or something, like DOS TSRs worked in the past. In DOS, the TSR was responsible for chaining the interrupt, in this, the framework does the chaining, but the handler can advise the framework of how to proceed.
The code is functional, but the features are limited. You will find graphics primitives as defined below.
The project is straight C with PASM drivers.
Having read through the above and spent a few hours on the internet, I think what I want is a language that is simple and where you can change things in "runtime". vb6 is not 100% runtime - you still have to draw some dummy imageboxes on the screen. But you can always have a pile of spare textboxes and buttons and just have them all invisible to start.
Then draw them and resize them in code. So in vb6, the code might be
and that draws a background and then some pictures. The last little bit tests that you can click on pictures and do something. That is the event_payload discussed above.
@jazzed, yes I agree, a set of simple routines to draw buttons etc. I've got some that do things pixels/lines at a time, and others that mainly deal with bitmaps (eg 4 corners of a button might be easier in bitmap if the corners are rounded).
So - the Spin and the Basic are fairly similar so long as you have routines behind the scenes to draw things, load bitmaps etc.
This is spin loading the background and then icons (more icons than in vb6 but this is just a demo)
This code is also blending the icon into the background using a pasm routine. That probably is not necessary on the PC side but I suppose it could be done using the mask.
So the picture on the PC and the picture on the Propeller look rather similar. The one on the PC is overlying the code.
Once the pictures are loaded, the programs go off and do two different things. On the Propeller, it is reading the SPI port waiting for a touch input. That returns X and Y. (Or it could be running a mouse object). On the PC, the code scanning for mouse input is invisible to the user, but I think it might be possible to make it part of a routine that also returns X and Y. (addit, yes, see below) Then the code becomes more similar.
Making code similar is the key here because it makes the translation easier.
So - in vb.net you could detect a mousedown on an icon but that is cheating because there is some background going on there where vb6 already knows where the icon is. So instead, detect that the click was somewhere on the picture "image1"
and then pass x and y to the same routine the touchscreen has. (which will be a new routine that remembers where it drew the icons and how big they are so it knows that is a hotspot on the screen).
So this makes it easier to debug code on the PC without having to download all the time.
So the next bit - convert vb6 to C. That is going to need a text parser. But it could be a hybrid of a custom text parser and then an existing generic Basic to C converter.
Even better, instead of vb6, I wonder what sort of early GUI editing suites are around for C from the 1990s? Because it would be easier to convert C to a different sort of C than Basic to C. I'm not an expert on the history of C but wikipedia helps out with http://en.wikipedia.org/wiki/Visual_C%2B%2B I wonder if they were incrementing the version numbers the same as visual basic, ie visual C++ 6.0
eg in vb6, the controls are image, label, text, frame, button, check, radio, combo, listbox, scrollbars, timer, filelistbox, shape and line.
In code, things like "LoadPicture" simply replaces the existing code have for reading a .bmp off the propeller SD card. So hopefully as much as possible, it is a matter of replacing functions with differently named functions. eg LoadPicture on the PC is going to need a string for the filepath "c:\mydirectory\mysubdirectory\myfile.bmp" and for the propeller version, everything might be in the root directory. So you could have a global string AppPath and change it once at the beginning. Or maybe not - I think some boffins have managed to get subdirectories working too.
So - looking for a C++ editor from the late 1990s that is free and has a GUI that only has the basic controls.
This is very exciting. See, Spin has two big disadvantages. You can't run it on a PC, and you can't run big programs. However, C++ opens up so many more possibilities because you can run big programs, and if we are careful about coding, it should be possible to run similar code on a PC (with mouse) and a Propeller (with touchscreen).
Hmm - if you are a programmer I think you can get access to old microsoft products http://www.microsoft.com/BizSpark/ Addit - not the easiest website to navigate - a search for "visual C" turns up zero hits?! Anyway, some nice chap in Sweden has let me borrow a copy of his version of Visual C++ 6.0 I'm on the steep part of a learning curve again. It is 35 files for a simple "Hello World", not 3 like in VB. You can get right into the insides of Windows with this C (which Visual Basic keeps hidden) which may or may not be needed here and I might need to go back to some earlier versions of C. All the windows stuff is a bit of a distraction, especially if one thinks about running this on Linux as well. Ideally we want a program that abstracts the buttons/textboxes etc at the right place - not too abstract, but not too detailed either.
Come to think of it, the answer to that question might be "html".
That may be the case. Question is how big/complicated will a simple html browser be?
Also remember that typical C++ programs are huge! You can write MCU class C++ programs with Propeller GCC, but most existing PC class C++ programs will be impractical.
Why don't you try Eric's Spin to C++ translator?
Maybe there is a VB6 to C++ translator?
So I've recoded in vb.net. Some things are very similar to spin - eg in Spin, call an spi driver to read the touchscreen. Returns x and y. In vb.net
which also returns x and y. I have been thinking about trying to translate things line by line but the structure is very different at the lower level (ie a touchscreen vs a mouse) so it may be easier to abstract that back to a function that does the same thing, and then drop in different functions as the code is translated from vb.net to C.
In some ways vb.net and spin are very similar. Sometimes just a matter of square vs curved braces and the odd @ symbol. vb.net code with spin underneath commented out.
In vb.net it is easy to emulate hub arrays in vb.net and simply replicate the external ram as an array as well. So then things can be debugged more accurately, eg rather than using a standard windows font, take the font files being used on the propeller, read them into an array, then display them pixel by pixel on the vb.net picturebox.
Some things have to be done so differently that they would be better replacing whole functions rather than translating lines one at a time.
eg reading in a file to the external ram array in vb.net
but in Spin, the SD card works best in blocks of 512 bytes, so to get data out to the external ram, it has to be read into a hub array 512 bytes at a time, then those blocks moved out to external ram. Then handle the remainder at the end. This routine is used for things like font and bitmap files which can be over 100k in size, and where access from an SD card each time is too slow.
I don't know about doing this in GCC - at the fundamental level it still will probably be like Spin - move things in blocks.
I'm not quite sure where this is heading - it might even be able to output Spin and C as part of the same compile process.
Why do you need VB code? Prototyping on the PC?
What is the end goal?
I'm not quite sure!
I guess I am starting with the things we can't do yet and trying to solve them.
The first thing is autocomplete in an IDE. Type "textbox1" and it automatically changes to "TextBox1". Your IDE could probably do that. Then if you type a full stop/period . a dropdown menu appears. This means you don't have to remember all the functions in that object. In fact, rather than go to the help for an object, I often use the dropdown menu and search through till I find the correct function. Fundamentally there is no reason why a Spin IDE could not do this too. And to be slightly more controversial, every public function in a .h header file as well.
The second thing is the way you can write a program by dragging things to a form rather than writing text. I'm not even sure how to write such a program, though I'm sure it can be done. See http://en.wikipedia.org/wiki/Visual_Basic and scroll down to the History section about half way down. That was done in 1991 so maybe we can replicate it? So an alternative is to incorporate an existing IDE, ie VB.net or C#, and modify it to output C++ code (and possibly Spin and Pasm).
Bottom line is making the user experience even more intuitive. For the calculator program I wrote in spin, instead of starting by writing code, start with a picturebox, and drag in the picture of the calculator. Then drag all the images of the buttons. Click on a textbox and set the font. Then click on those buttons and finally write the code.
I suppose I am trying to make C++ into a more graphical language?
Maybe a simple page description markup language would work.
I've looked around considering different methods, and I think Qt Quick is "propably" perfect.
Notice the image support and the onClicked callback.
QtQuick supports touch screen flicking, flipping, and pinching too.
The "QML" output is very simple. It might be parsed and rendered with a small propeller program.
This was made with QtCreator in a few minutes. The image was produced by clicking Qt debug.