Shop OBEX P1 Docs P2 Docs Learn Events
Graphical languages on the Propeller — Parallax Forums

Graphical languages on the Propeller

Dr_AculaDr_Acula Posts: 5,484
edited 2012-08-17 00:05 in Propeller 1
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
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.
800 x 600 - 50K
1022 x 714 - 72K

Comments

  • CircuitsoftCircuitsoft Posts: 1,166
    edited 2012-08-15 05:55
    What spin is missing to support that type of code is run time heap allocation. In spin, all objects are allocated at compile time, so allocation issues become (mostly) compiler errors. In C/C++, malloc() allows run-time creation of objects. Given the housekeeping overhead of an allocator, it may be better to try to avoid that on the prop.

    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.
  • jmgjmg Posts: 15,183
    edited 2012-08-15 14:54
    Dr_Acula wrote: »
    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.

    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.. :)
  • CircuitsoftCircuitsoft Posts: 1,166
    edited 2012-08-15 15:18
    Not really. Oracle has made it clear that use of Java results in lawsuits, so I would stay as far from it as possible.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-15 18:09
    Some great ideas there, thanks!

    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!
  • jazzedjazzed Posts: 11,803
    edited 2012-08-15 20:11
    Sounds neat.

    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
  • CircuitsoftCircuitsoft Posts: 1,166
    edited 2012-08-15 21:26
    I imagine there's a lot to be learned from Macintosh Folklore, especially the Resource Maker. While the stories on this site are more about how people interact, there are some technical details, and the machine did actually limit memory segments to 32k. It's kind of amazing what they managed with that.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-15 22:53
    Thanks jazzed. What is the QtQuick widget for?

    @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
    CON
      _clkmode      = xtal1 + pll16x                        ' use crystal x 16
      _xinfreq      = 5_000_000
    OBJ
      tch:           "Touch"                                 ' touchscreen driver
      obj1:          "Object1"                               ' demo object
    
    VAR long memblocks[100]                                ' location of memory blocks for objects
      
    PUB Main
       tch.BeginProgram
       obj1.start(@memblocks)
       obj1.addfive   
       tch.hex(memblocks[0],8)
    

    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
    VAR
        long memblocks
    
    PUB Start(n)
        memblocks := n              ' store in this objects global variable list
    
    PUB AddFive
        long[memblocks] := long[memblocks] + 5   ' shows reading and writing a variable in another 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!
  • wjsteelewjsteele Posts: 697
    edited 2012-08-16 13:12
    I think Rayman's VisualSPIN editor fits part of what you're asking... it is a graphical designer for spin, which is very much like the old VB was.

    Here's the link: http://www.rayslogic.com/propeller/Programming/VisualSpin/VisualSpin.htm

    Bill
  • msrobotsmsrobots Posts: 3,709
    edited 2012-08-16 22:27
    @Dr_Acula,

    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
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-08-16 22:33
    Thanks Mike. That looks interesting. I am working along the same lines and I like the idea of parsing it off to external memory in a way that is hidden from the user. More coffee required...
  • jazzedjazzed Posts: 11,803
    edited 2012-08-17 00:05
    Dr_Acula wrote: »
    Thanks jazzed. What is the QtQuick widget for?

    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.
Sign In or Register to comment.