Graphical languages on the Propeller
Dr_Acula
Posts: 5,484
Well I have this crazy idea that we can run graphical language on the propeller and I'd like some expert advice on which language and technical help with classes and objects. See the photos for an example.
Take a GUI IDE like C# or vb.net or Java. Draw a form, put a button on the form, drop in a text box, write a few lines of glue code, hit the run button and it prints "Helloooo World". Oops, spelling error, debug on the PC, fix the error (takes a few seconds), run again and it is all ok on the PC. Very quick run/debug/rerun cycle because there are no downloads.
Now - run a batch file, and download and run the exact same program on the Propeller.
Can this be done?
Well, there are lots of ways to do this, including things like porting .net micro over to the propeller and using various language translation programs. I've been experimenting with one solution that hacks into the .net files.
Here is a vb.net program
and it is almost identical in C#.
Please bear with me, this is fun!
Ok, now that is not the entire program. The "program" is contained in a file called "form1.vb". But it is coupled with a few other files, a .resx file that contains things like the actual bitmaps of any pictures that might be in the program, and a file called "form1.designer.vb". This last file contains information about where the objects are located. This is the complete file for our simple "Hello World":
We can discard a lot of that. But contained within that text are the locations of the button and the text box and the size of those text boxes. Fonts are not there because it assumes the default font, but if you cange the font then a new line appears there with the font information.
So - the main form is 232 by 293 pixels. Actually, in vb.net, I set that to be 240x320, but there is a boundary and a menu bar along the top, so it ends up a bit smaller. Those things can be adjusted for, and the menu happens to be 22 pixels high. So the button is at location 68,37. We can draw that in Spin with
where menuheight is set to 22, 0 is a location to point to a string with the text of the button, 5 is a location of a bitmap file of the button and f2 is the location of the font to draw the text for the button. It is a bit messy, and not at all object oriented (which is where I'd like some help) but it does draw a button.
There is enough information in those two text files above to create a Spin program. I created the Spin program by hand, but I think it can be easily automated, and it probably would be better in C++ with a more object oriented structure.
So - we take the information about the location of the objects and use that to draw some objects on the screen, and then we fire up the touchscreen and wait for a keypress, and when the user touches the button, the textbox is updated with "Hello World". In Spin
See the attached pictures. The vb.net program (or C# if you want) is not quite the same as the propeller program, but it is pretty close. And yes, I cheated a bit and did the background as one bitmap rather than building up the menu in sections.
So that is all very clever, but the problem is the .net code and the Spin code are vastly different.
What I am wondering is whether we can make them more similar, and in particular, make the Spin more object oriented.
Take the very simple statement
That appears to hide a lot of complexity. It certainly tells something that we are creating a new button. But behind the scenes, it is reserving some memory for the descriptors of that button. The location, the font size etc. In 'old school' language, it is dimensioning an array on the fly within code, and the array might contain values and strings and even collections of data. In Spin, that is a bit complicated. You might be reserving half the hub ram just there with that line. Further, you are creating an object that might need to not just be accessed from the main program, but also from sub-objects. And internal values are going to need to be static which implies VAR values where you don't even know how many you might need. This starts to push up against the limitations of the "object oriented" nature of Spin.
There are workarounds in Spin of course. With external memory, you can reserve space, and store lookup tables for objects so that any code from any sub object can find the data. But that involves writing code to handle these more sophisticated objects, and in any case, sooner or later one runs out of code space.
I have this vague feeling that C++ is going to be a lot better suited to this problem. I don't quite understand C++ but I think it is more object oriented. So you can hide more things from the user and use abstract concepts like creating a new button without having to bother the user with exactly how much ram was reserved.
So I think where this is going to morph is from a vb.net to spin conversion to a C# to C++ conversion. I think it needs to evolve from a program revolving around functions, to a program revolving around objects. At least for the user anyway. Behind the scenes, it is still a bunch of functions that draw lines and pixels.
Thoughts and advice would be most appreciated.
Take a GUI IDE like C# or vb.net or Java. Draw a form, put a button on the form, drop in a text box, write a few lines of glue code, hit the run button and it prints "Helloooo World". Oops, spelling error, debug on the PC, fix the error (takes a few seconds), run again and it is all ok on the PC. Very quick run/debug/rerun cycle because there are no downloads.
Now - run a batch file, and download and run the exact same program on the Propeller.
Can this be done?
Well, there are lots of ways to do this, including things like porting .net micro over to the propeller and using various language translation programs. I've been experimenting with one solution that hacks into the .net files.
Here is a vb.net program
Public Class Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click TextBox1.Text = "Hello World" End Sub End Class
and it is almost identical in C#.
Please bear with me, this is fun!
Ok, now that is not the entire program. The "program" is contained in a file called "form1.vb". But it is coupled with a few other files, a .resx file that contains things like the actual bitmaps of any pictures that might be in the program, and a file called "form1.designer.vb". This last file contains information about where the objects are located. This is the complete file for our simple "Hello World":
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _ Partial Class Form1 Inherits System.Windows.Forms.Form 'Form overrides dispose to clean up the component list. <System.Diagnostics.DebuggerNonUserCode()> _ Protected Overrides Sub Dispose(ByVal disposing As Boolean) Try If disposing AndAlso components IsNot Nothing Then components.Dispose() End If Finally MyBase.Dispose(disposing) End Try End Sub 'Required by the Windows Form Designer Private components As System.ComponentModel.IContainer 'NOTE: The following procedure is required by the Windows Form Designer 'It can be modified using the Windows Form Designer. 'Do not modify it using the code editor. <System.Diagnostics.DebuggerStepThrough()> _ Private Sub InitializeComponent() Me.Button1 = New System.Windows.Forms.Button Me.TextBox1 = New System.Windows.Forms.TextBox Me.SuspendLayout() ' 'Button1 ' Me.Button1.Location = New System.Drawing.Point(68, 37) Me.Button1.Name = "Button1" Me.Button1.Size = New System.Drawing.Size(84, 30) Me.Button1.TabIndex = 0 Me.Button1.Text = "Button1" Me.Button1.UseVisualStyleBackColor = True ' 'TextBox1 ' Me.TextBox1.Location = New System.Drawing.Point(59, 123) Me.TextBox1.Name = "TextBox1" Me.TextBox1.Size = New System.Drawing.Size(118, 20) Me.TextBox1.TabIndex = 1 ' 'Form1 ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.ClientSize = New System.Drawing.Size(232, 293) Me.Controls.Add(Me.TextBox1) Me.Controls.Add(Me.Button1) Me.Name = "Form1" Me.Text = "Form1" Me.ResumeLayout(False) Me.PerformLayout() End Sub Friend WithEvents Button1 As System.Windows.Forms.Button Friend WithEvents TextBox1 As System.Windows.Forms.TextBox End Class
We can discard a lot of that. But contained within that text are the locations of the button and the text box and the size of those text boxes. Fonts are not there because it assumes the default font, but if you cange the font then a new line appears there with the font information.
So - the main form is 232 by 293 pixels. Actually, in vb.net, I set that to be 240x320, but there is a boundary and a menu bar along the top, so it ends up a bit smaller. Those things can be adjusted for, and the menu happens to be 22 pixels high. So the button is at location 68,37. We can draw that in Spin with
bu1x := 68 bu1y := 37 DrawButton(bu1x,bu1y+menuheight,0,5,f2) ' button
where menuheight is set to 22, 0 is a location to point to a string with the text of the button, 5 is a location of a bitmap file of the button and f2 is the location of the font to draw the text for the button. It is a bit messy, and not at all object oriented (which is where I'd like some help) but it does draw a button.
There is enough information in those two text files above to create a Spin program. I created the Spin program by hand, but I think it can be easily automated, and it probably would be better in C++ with a more object oriented structure.
So - we take the information about the location of the objects and use that to draw some objects on the screen, and then we fire up the touchscreen and wait for a keypress, and when the user touches the button, the textbox is updated with "Hello World". In Spin
CON _clkmode = xtal1 + pll16x ' use crystal x 16 _xinfreq = 5_000_000 OBJ tch: "Touch" ' touchscreen driver PUB Main | touchtest ' debug value tch.BeginProgram ' display type 2 = dual ILI9325, false means run a program not the desktop so don't load icons Buttons tch.SetWarmBoot ' clears screen, sets a warm boot and reboots PUB Buttons | bu1x,bu1y,xval,yval,f1,f2,menuheight menuheight := 22 ' menu at the top of a form tch.SetOrientation(true) ' portrait view tch.ClearRam ' file 0 and 1 reserved tch.SDBMPtoRam(string("form1.bmp")) ' file 2 tch.DrawBMPRam(2,0,0) ' draw the background picture f1 := tch.Loadfont(string("MSans14W.ifn")) ' file 3 black on white font f2 := tch.Loadfont(string("MSans14.ifn")) ' file 4 black on gray 14 point seems to be the same as 8.25 in vb.net ? why tch.SDBMPtoRam(string("butn8430.bmp")) ' file 5 a button 84x30 in size tch.stringdim(10) ' file 6 is the strings tch.stringstore(0,string("Button1")) ' store string for the ok button bu1x := 68 bu1y := 37 DrawButton(bu1x,bu1y+menuheight,0,5,f2) ' button (fixed size at the moment) Textbox(59,123+menuheight,59+118,123+16+menuheight) ' clear a text box. height = fontheight+1? ' now wait for touch tch.SelectSPIGroup ' talk to the spi touchscreen repeat yval := tch.TouchYPercent ' decode yval 0-100% xval := tch.TouchXPercent ' decode xval 0-100% if (xval <> 255) and (yval <> 255) ' valid keypress xval := (xval * 24) / 10 ' scale to pixels as can measure from the picture in paintshop yval := (yval * 32) / 10 ' scale to pixels if xval >68 and xval <(68+84) and yval>(37+menuheight) and yval<(30+37+menuheight) tch.SelectMemGroup ' display mode tch.setcurx(59+2) tch.setcury(123+menuheight) ' y = y1 in textboxclear tch.TextT(f1,string("Hello World")) ' draw text for the textbox tch.SelectSPIGroup ' talk to the spi touchscreen PUB TextBox(x1,y1,x2,y2) ' draw the outline of the box tch.ClearRectangle(x1,y1,x2,y2,tch.RGBtoWord(255,255,255)) tch.Drawline(x1-1,y1-1,x2,y1-1,tch.RGBtoWord(64,64,64)) ' gray outline lines - extract values from paint shop pro tch.Drawline(x1-2,y1-2,x2+1,y1-2,tch.RGBtoWord(128,128,128)) ' horizontal upper light gray tch.Drawline(x1-1,y1-1,x1-1,y2,tch.RGBtoWord(64,64,64)) ' vertical left gray tch.Drawline(x1-2,y1-2,x1-2,y2+1,tch.RGBtoWord(128,128,128)) ' vertical left light gray tch.Drawline(x2+2,y1-2,x2+2,y2+2,tch.RGBtoWord(255,255,255)) ' vertical right white tch.Drawline(x1-2,y2+2,x2+2,y2+2,tch.RGBtoWord(255,255,255)) ' horizontal lower white PUB DrawButton(x,y,stringn,c1,f) ' draw a button box tch.DrawBMPRam(c1,x,y) tch.setcurx(x + (84/2)-(tch.stringlen(stringn)*4 / 2)) ' centre, avg width 4 per char 84 could be a variable width passed to this routine tch.setcury(y + 8) tch.StringPrint(f,stringn) ' print
See the attached pictures. The vb.net program (or C# if you want) is not quite the same as the propeller program, but it is pretty close. And yes, I cheated a bit and did the background as one bitmap rather than building up the menu in sections.
So that is all very clever, but the problem is the .net code and the Spin code are vastly different.
What I am wondering is whether we can make them more similar, and in particular, make the Spin more object oriented.
Take the very simple statement
Me.Button1 = New System.Windows.Forms.Button
That appears to hide a lot of complexity. It certainly tells something that we are creating a new button. But behind the scenes, it is reserving some memory for the descriptors of that button. The location, the font size etc. In 'old school' language, it is dimensioning an array on the fly within code, and the array might contain values and strings and even collections of data. In Spin, that is a bit complicated. You might be reserving half the hub ram just there with that line. Further, you are creating an object that might need to not just be accessed from the main program, but also from sub-objects. And internal values are going to need to be static which implies VAR values where you don't even know how many you might need. This starts to push up against the limitations of the "object oriented" nature of Spin.
There are workarounds in Spin of course. With external memory, you can reserve space, and store lookup tables for objects so that any code from any sub object can find the data. But that involves writing code to handle these more sophisticated objects, and in any case, sooner or later one runs out of code space.
I have this vague feeling that C++ is going to be a lot better suited to this problem. I don't quite understand C++ but I think it is more object oriented. So you can hide more things from the user and use abstract concepts like creating a new button without having to bother the user with exactly how much ram was reserved.
So I think where this is going to morph is from a vb.net to spin conversion to a C# to C++ conversion. I think it needs to evolve from a program revolving around functions, to a program revolving around objects. At least for the user anyway. Behind the scenes, it is still a bunch of functions that draw lines and pixels.
Thoughts and advice would be most appreciated.
Comments
What I can see doing is defining GUI elements as ID numbers and having a DAT section that gets filled with a display-list structure to define a dialog. Buttons and such can even have callback pointers with defined function definitions in them. That would be similar to dialog definition under classic windows GDI.
Have you seen this new announcement ?
http://www.eetimes.com/electronics-news/4394110/Oracle-releases-Java-SE-7-Update-6--expands-OS-support-to-Mac-OS-X--Linux-JavaFX
Sounded topical..
Re memory allocation - I've been reading this http://en.wikipedia.org/wiki/Memory_management
When I first started using external memory on the Prop I was looking at creating a ram disk, and I even had quite a bit of the CP/M file system coded to act as a ram disk. But the way touchscreens work, it is much more efficient to have all the data contiguous. And if you are going to do that, it is better to allocate display buffers for bitmaps at the beginning of the program and then never revisit them. If one does look at a memory allocation system, the chunks need to be fairly large as there is an overhead setting up the addresses for a memory access. This would need to be done for any object oriented program. For Spin, I think it requires some drivers to move things from hub out into external ram.
Writing all this in XMMC may well be a lot easier, as the program lives in external memory so that frees up all the hub for data. And I am really hoping that this sort of memory allocation has been thought about already in C and that there are routines that can be used out of the box. I guess one would need to write the low level routines (in pasm) to handle memory transfers, and the C code handles things at a higher more abstract level.
At the simplest level, create a new button. That calls an object (or a function that is linked to other functions) that allocates some external memory for all the information about that button. It also increments an internal static variable that counts how many buttons there are.
I think I can start to see why object oriented programming was invented. I can also see how it bumps up against a few problems with memory limitations in embedded controllers.
With Spin, I am trying to get my head around making it more object oriented. Consider a "main" function and object1 and object2. Object1 does buttons and object2 does radioboxes. We create some buttons. Now we create a radiobox. Object2 needs to know how many buttons there are in order to work out which memory it can use. That involves communication between objects, and I'm not sure Spin does that natively. I think what you need to do is create an array that contains some variables that will be common to all objects, then when starting up all the objects, tell them where that array is. In C, pass a bunch of pointers.
More coffee required!
I'm still thinking about using a data-structure to store QtQuick widget info and using an interpreter to render it. Still writing mundane platform code for the project though.
Looking forward to seeing your progress.
--Steve
@circuitsoft, I never realised the mac had that little memory. Ok, here's one out of left field. Kye has written a program that can load a binary off the SD card and run it. So you can chain one program to the next to the next. Use the external ram as a link between them. But the catch is that it takes 1-2 seconds to read 32k off the SD card. But the touchscreen can read 32k from external ram in maybe 10 milliseconds. So chaining programs could be quite feasible. How cool would it be to move blocks of pre-compiled spin in and out of a predefined section of hub ram? Can we trick spin in that way? Put in some dummy code and pad it out to maybe 10k or something, then move in different code to that location. Would it work if all programs (reloadable objects) all started with a list of PUBs with calls - PUB,call,PUB,call. Call the third PUB in the list, which will be different depending on which object is loaded.
I'm trying something that is closer to C++ and C# as I think that will make the porting process easier, plus it isn't going to be as tricky as trying to hack Spin.
First thing is getting common static variables between objects. So you add three button objects and that allocates some memory and then you add a textbox and the textbox object knows where to start storing its data.
Nothing particular clever here but it does show how to create static variables across objects
in the Main
and in the object, send the location of the external ram pointer in a start method, and then that is now stored in the local common variable space for that object
I'm going to try to avoid nesting objects. But I would like to have the button as one object, and the textbox as another object.
Next challenge is how to get all those disparate objects to talk via the same graphics driver. At the moment, the graphics driver (tch) belongs in the main program. It wastes memory to replicate that in every object. So need a graphics object that is polling memory locations waiting for changes. Or - every time you talk to an object from the main program, you then call a function to update any graphics changes.
Good brain exercise!
Here's the link: http://www.rayslogic.com/propeller/Programming/VisualSpin/VisualSpin.htm
Bill
there is a APPnote from Parallax and some spin. Demo fro GUI-elements with a nice even-handler-system, properties etc.
But you can not create a button dynamically - at least you need to include one button-object for ech button at compiletime
aka
OBJ
buttons[5] : "buttons.spin"
this was the breaker for me so I never looked at it again.
But a while ago I started working on some 'PropDatabase' and was in need of Struktures or Objects to represent a reccord in my table.
doing that with
OBJ
adresse[5000] : "adresse.spin"
will not work obviously. After pondering this for a while I came to some workable solution.
my db object does neither has VAR nor DAT space to store anything. Just constants as offset and get/set accessors. To use it you poit it to the start of the 'reccord' from db sitting in hub-ram.
access lookes like this:
... reading reccord from db/external ram into buffer named recordbuffer
textbox.SetBase(@recordbuffer)
textbox.SetID:=12345
textbox.SetX:=56
textbox.SetZ:=34
... saving reccord to db/external ram
inside of object textbox I have one var to hold my baseadress given in load ( so yes I do have one VAR...) and constants defined in CON to hold the offsets. the PUB methods for GetXX and SetXX just add the offset to thye vbase and have the data needed.
so defining in
CON
_ID = 0
_X = 4
_Y = 8
VAR
long base
PUB SetBase(adress)
base:=adress
PUB SetID(ID)
long[base+_ID ]:=ID
PUB GetID
RETURN long[base+_ID]
...
Stringhandling is more complicated, but given a fixed number of bytes for strings It works fine
so you can define a object TextBox with all code to do what you need just provide the VAR part as Memory-Block.
one object and multiple Instances... just Set the base-pointer...
My 2 cents on this...
Enjoy!
Mike
A QtQuick widget is a GUI item described in a very simple block of markup code (or a subset of it) generated by the QtDesigner (a GUI interface designer). The idea is to parse and render widgets (pictures/buttons, text edit controls, progress bars, action swipes, etc....) with Propeller code that has pre-set callbacks for user interaction. I've shown an example of this before, but haven't had much time to implement it. I'm edging closer to it these days.