[Tutorial] Reusable objects on multiple screens

General discussion on Visual TFT Software.
Post Reply
Author
Message
aCkO
Posts: 1119
Joined: 14 Feb 2011 04:07
Location: Bar, Montenegro

[Tutorial] Reusable objects on multiple screens

#1 Post by aCkO » 10 Oct 2013 22:53

In many aspects, Visual TFT and Visual GLCD are very similar to popular IDEs like VB, Delphi, C# etc. Each of them provides basic GUI object called Form. Each Form contains Components/Controls which are used for displaying data and user input (Buttons, TextBoxes, ComboBoxes, CheckBoxes etc.) Each Form maintains a list of components it owns. For example, in Delphi if you wanted to iterate through Form components you would do something like this:

Code: Select all

for i := 0 to ComponentCount - 1 do
begin
  if Component[i] is TButton then
     // do something with button

  if Component[i] is TLabel then
     // do something with label
  ...
end;
The equivalent of Form in VTFT/VGLD is Screen object. Unfortunately, the resemblance ends there because of the way components/objects are handled in the definition and driver files.

If you examine generated code for the definition of Screen object in an application containing multiple object types (Buttons, Labels, CheckBoxes etc.) you will see something like this:

Code: Select all

typedef struct Screen TScreen;

struct Screen {
  unsigned int           Color;
  unsigned int           Width;
  unsigned int           Height;
  unsigned int           ObjectsCount;
  unsigned int           ButtonsCount;
  TButton                * const code *Buttons;
  unsigned int           LabelsCount;
  TLabel                 * const code *Labels;
  unsigned int           ImagesCount;
  TImage                 * const code *Images;
  unsigned int           BoxesCount;
  TBox                   * const code *Boxes;
  unsigned int           CheckBoxesCount;
  TCheckBox              * const code *CheckBoxes;
  unsigned int           RadioButtonsCount;
  TRadioButton           * const code *RadioButtons;
};
Screens containing more/less component types will have more/less members in the structure. Anyone familiar with object-oriented programming (OOP) will right away conclude that this approach is wrong. In OOP, object definition cannot change under any circumstance. If it does, than we are talking about multiple objects, not one. Delphi/VB/C# use 2 properties to keep track of all components on the form: Count and Components/Controls array. We could also do that in the object definition file (*_objects.h):

Code: Select all

typedef struct Screen TScreen;

struct Screen {
  unsigned int           Color;
  unsigned int           Width;
  unsigned int           Height;
  unsigned int           ObjectsCount;
  const TComponent       *Components;   // pointer to const TComponent (first element of Components array)
};
Now, Screen object resembles more to Delphi/VB/C# Form and we got rid of 12 unnecessary variables! (2 for each object type). Let's define TComponent like this:

Code: Select all

typedef enum {_objButton, _objLabel, _objImage, _objBox, _objCheckBox, _objRadioButton} TObjectType;

typedef struct {
   TObjectType ObjType;
   void *ObjPtr;
} TComponent;
TComponent has 2 members used to identify the Component: pointer to it and its type (used to determine size). Now we have all we need to identify the component in RAM/ROM and iterate through Screen components just like in the Delphi example from the beginning of this post:

Code: Select all

const TComponent  Screen1_Components[] = {
     {_objBox, &Box1},
     {_objLabel, &Label1},
     {_objButton, &Button1},
     {_objCheckBox, &CheckBox1},
     {_objRadioButton, &RadioButton1},
     {_objImage, &Image1}
};

Screen1.Components = Screen1_Components;

for (i = 0; i < Screen1.ObjectsCount; i++) {
   if (Screen1.Components[i].ObjType == _objButton)
      // do something with button
   if (Screen1.Components[i].ObjType == _objLabel)
      // do something with label
   ...
}
Now let's clean up unnecessary object properties that make our life difficult and prevent us from reusing objects on multiple screens:

Code: Select all

typedef struct  Button {
   TScreen*  OwnerScreen;
   char          Order;
   ...
Each object in VTFT has Order property which, as the name suggests, is used by the DrawScreen function to indicate the order in which the component is drawn:

Code: Select all

while (order < CurrentScreen->ObjectsCount) {
    if (button_idx < CurrentScreen->ButtonsCount) {
      local_button = GetButton(button_idx);
      if (order == local_button->Order) {
        button_idx++;
        order++;
        DrawButton(local_button);
      }
    }

    if (label_idx < CurrentScreen->LabelsCount) {
      local_label = GetLabel(label_idx);
      if (order == local_label->Order) {
        label_idx++;
        order++;
        DrawLabel(local_label);
      }
    }
    ...
}
Since we created one array of TComponent objects we don't need Order property. Why? Because we don't use an array for each object type. Instead, we have only one array and we use element index to determine the order in which the component is drawn:

Code: Select all

while (order < CurrentScreen->ObjectsCount) {
     switch (CurrentScreen->Components[order].ObjType) {
        case _objButton:
           DrawButton((TButton *)CurrentScreen->Components[order].ObjPtr);
           break;
        case _objLabel:
           DrawLabel((TLabel *)CurrentScreen->Components[order].ObjPtr);
           break;
        ...
     }
     order++;
}
We finished the drawing part and the code already looks much nicer. Now we need to take care of the events. If you trace the function calls from the start of the program you will see that Check_TP() is called from an infinite loop in main. Check_TP calls 3 functions which then call the appropriate event handlers: Process_TP_Press, Process_TP_Up and Process_TP_Down. Each of them has a call to Get_Object function which determines the object you are interacting with through the touch screen. I won't go into details because this post is already too long. Just check the example in the attachment. I have intentionally commented the mE code for you to see what changes I have made. The final result is the removal of around 20 global variables and almost 200 lines of code in the driver file. The RAM and ROM usage is much lower than in the original example.

And finally, here's how you reuse objects on another screen:

Code: Select all

const TComponent myComponents[] = {
                                     {_objBox, &Box1},
                                     {_objLabel, &Label1},
                                     {_objButton, &Button1},
                                     {_objCheckBox, &CheckBox1},
                                     {_objRadioButton, &RadioButton1},
                                     {_objImage, &Image1},
                                     {_objLabel, &Label2}
                                  };


void Button1Click() {
   if (CurrentScreen == &Screen1) {

      Screen2.Components = myComponents;
      Screen2.ObjectsCount = sizeof(myComponents)/sizeof(TComponent);

      DrawScreen(&Screen2);
   }
}
Remember that the driver and objects definition files get overwritten each time you recreate the project in VTFT. Also, you still need to configure the reusable objects in code but that should not be a problem. The best solution right now is to hope that some of mE developers will take into consideration ideas presented here. The reusability can easily be achieved by introducing concepts like "Application Pool Components" and "Screen Pool Components" and adding Screen OnLoad event which will take care of component setup. Tomorrow I will upload examples for mikroBasic and mikroPascal.

Regards
Attachments
Reusable Objects VTFT.7z
(28.47 KiB) Downloaded 514 times

Megahurts
Posts: 900
Joined: 01 Oct 2009 22:48
Location: Rocky Mountains, USA.

Re: [Tutorial] Reusable objects on multiple screens

#2 Post by Megahurts » 11 Oct 2013 01:36

Hi Aleksandar,

Wow, and excellent work again buddy. :mrgreen:

Did not expect it done so soon, but glad you did. I was planning on forgetting about it until I got done with my current project for the community beginners in V-TFT.
It is almost done anyway, and my personal project that I want to try this on is calling me back again.

Looked over your material briefly and DL'd the file. But will probably have to wait for the Basic version to be able to fully grasp implementation practice needed.

Quick question though, do you think it is better/easier to do this at start of a project or near end so there is minimal normal editing in V-TFT left to do
and less chances to mess things up?

Thanks again Bro, awesome information to enlighten us about V-TFT workings, Robert.
HW: easyPIC5|PICFlash2|easyBT|smartGSM|easyGSM|PICPLC16|mmWorkStation|FT800 Eve|PIC Clicker/2|
MMBs:PIC18F,PIC33EP,PIC32|CLICKs:DAC,ADC,GPS L10,Thermo,8x8B LED,Stepper,W/B OLED,9DOF,GPS3,tRF,Hall I|

SW: mP for PIC|mB for PIC-dsPIC-PIC32|Visual-TFT|

JohnGB
Posts: 183
Joined: 17 Feb 2013 18:59

Re: [Tutorial] Reusable objects on multiple screens

#3 Post by JohnGB » 11 Oct 2013 10:04

A much more elegant approach than that used by mE although I doubt mE's developers will take that on board. Priority seems more about supporting new chips than resolving the fundamental problems of V-TFT.

Megahurts - I might be wrong but I think you would have to design the whole UI first and then do what would be a pretty major edit to make components re-usable across a number of screens. You really don't want to have to redo the edits simply for a minor change to the UI.

I think Aleksandar was simply demonstrating a better way of doing things.
JohnB
MikroPascal for PIC18 and DsPIC, Visual TFT

MaGiK
Posts: 897
Joined: 19 Apr 2013 10:00

Re: [Tutorial] Reusable objects on multiple screens

#4 Post by MaGiK » 11 Oct 2013 16:20

This is a great tutorial aCkO! It's really helpful for a beginner like me :D
I really hope that you keep up the amazing work. It's much appreciated.
Best Regards
Last edited by MaGiK on 12 Oct 2013 05:43, edited 1 time in total.
My hobby is collecting MikroElektronika products.
Gotta catch them all!

aCkO
Posts: 1119
Joined: 14 Feb 2011 04:07
Location: Bar, Montenegro

Re: [Tutorial] Reusable objects on multiple screens

#5 Post by aCkO » 12 Oct 2013 03:18

Megahurts wrote:Quick question though, do you think it is better/easier to do this at start of a project or near end so there is minimal normal editing in V-TFT left to do
and less chances to mess things up?
As JohnGB suggested, you should make the described changes after you're done using VTFT and continue the work in compiler IDE. After you make the necessary changes, make a backup of *_objects.* and *_driver.* files. In case you need to change something again in VTFT and your code gets overwritten just replace the following functions from backup: DrawScreen, GetObject, Process_TP_Press, Process_TP_Down and Process_TP_Up. The rest of the editing can easily be made by attempting to compile the project and correcting errors from Messages window one by one.

You will find mikroBasic version of the project in the attachment.

Regards
Attachments
ReusableObjects_VTFT_mB.7z
(16.76 KiB) Downloaded 459 times

Megahurts
Posts: 900
Joined: 01 Oct 2009 22:48
Location: Rocky Mountains, USA.

Re: [Tutorial] Reusable objects on multiple screens

#6 Post by Megahurts » 17 Oct 2013 12:57

Thanks Aleksandar,

They need to make this thread a sticky also so it does not get lost in whatever exists after the first page list of thread topics lol. :wink:

Mhz.
HW: easyPIC5|PICFlash2|easyBT|smartGSM|easyGSM|PICPLC16|mmWorkStation|FT800 Eve|PIC Clicker/2|
MMBs:PIC18F,PIC33EP,PIC32|CLICKs:DAC,ADC,GPS L10,Thermo,8x8B LED,Stepper,W/B OLED,9DOF,GPS3,tRF,Hall I|

SW: mP for PIC|mB for PIC-dsPIC-PIC32|Visual-TFT|

Krell357
Posts: 1
Joined: 22 Nov 2013 07:30

Re: [Tutorial] Reusable objects on multiple screens

#7 Post by Krell357 » 22 Nov 2013 08:00

This looks absolutely perfect. All these tinny details are made with lot of backgound knowledge. I like it a lot. Keep on taking action.

Rotary_Ed
Posts: 756
Joined: 26 Dec 2004 23:10
Location: Matthews, NC, USA
Contact:

Re: [Tutorial] Reusable objects on multiple screens

#8 Post by Rotary_Ed » 02 Jan 2014 01:45

Happy New Folks

Thanks to Robert and Aleksandar, my journey into VTFT has been greatly aided.

After reading Robert's several tutorials and downloading Aleksandar's Reusable Ojbects, I was able to adapted it for my uses (no touch capability, just wanted the graphics part) for my needs.

I have approx 30 different screens with 5 buttons (always Buttons B1,B2,B3,B4,and B5) but depending on which screen the function carried out by the button varies and so does the brief descriptive Label along side the button (See attach jpg of one of the screen)

I divided my objects into two basic groups, RoundButtons and Labels, I then name these as my component resources, one was named My5Buttons and the other MyScreen1Labels. I then modified the drawscreen procedure such that you could provide two component inputs.

Here are my componenet collection of button and label objects

Code: Select all


const  // My univervals button ojbects - used repeatedly on all 30 + screens
      My5Buttoncomponents :array[Screen1_Array_size + 1] of Tcomponent = (

       (_objButtonRound,(@ButtonRound1)),      //must add here for each  object of even the same type
       (_objButtonRound,(@ButtonRound2)),
       (_objButtonRound,(@ButtonRound3)),      //both button component and label components arrays must be same size????
       (_objButtonRound,(@ButtonRound4)),        //with dummy? place holder
       (_objButtonRound,(@ButtonRound5)),
       (_objBlank,(@Blank)),                  //Blanks added so size of both My component arrays are same size
       (_objBlank,(@Blank)),
       (_objBlank,(@Blank)));

const   //MODE Screen  Labels - different for each screen
      MyScreen1Labels :array[Screen1_Array_size] of Tcomponent = (
        //must add here for each  object of even the same type
       (_ObjButton,(@Button1)),  //Screen Title
       (_objLabel,(@Label21)),  //Descriptive labels explaining button function
       (_objLabel,(@Label22)),
       (_objLabel,(@Label23)),
       (_objLabel,(@Label24)),
       (_objLabel,(@Label24)),
       (_objLabel,(@Label25)));

For some reason of which I am not certain, I spend some time before I discovered that the object arrays of the two component collection used to draw the buttons and labels separately, must have an equal number of objects even if some are dummy objects. That is why you see My5ButtonComponent array with 3 _objBlank,(@Blank)) dummy objects. I could not get the last label to draw on the screen until I did this.

Here is the screen making code:

The code I use for drawing the two set of component on the screen is as follows:

Code: Select all

         //Load Screen components with my components
            Screen1.Components := @My5ButtonComponents;
            Screen1.ObjectsCount := sizeof(My5ButtonComponents);
            Screen2.Components := @Myscreen1Labels;
            Screen2.Objectscount := sizeof(Myscreen1Labels);
            //Draw Objects on Screen
             DrawScreen2(@Screen1,@screen2);  //my modifed DrawScreen routine to Draw screen with both groups of components


            //** The  approach below works equally well without modifying the DrawScreen

            //Load button components and send to drawscreen
            //Screen1.Components := @My5ButtonComponents;
            //Screen1.ObjectsCount := sizeof(My5ButtonComponents);
           // DrawScreen(@Screen1);
            //Load Label Objects and send to drawscreen
           // Screen1.Components := @Myscreen1Labels;
          //  Screen1.Objectscount := sizeof(Myscreen1Labels);
            //DrawScreen(@Screen1);
          
The 3 attached photos shows the buttons and labels load to the screen separately and then both together

So thanks to you both, I have managed to get this far using your insight and code. But, here is the embarassing part :oops:
I can not seem to be able to find a command such as TFT_Fill_Screen that will work to erase the screen so I can draw the next screen with
My5ButtonComponents and a new/different MyScreen2Labels. I had previously used TFT_Fill_Screen(color) to in effect draw over and erase the current screen, but can not seem to get it to work for this.

The only thing that seems to erase the displays GRAM is when I use the mikroprog suite ERASE chip function.

I am using a KD024FM TFT (without touch function) which uses an ILI9341 display controller. My chip is a PIC32MX795F512H running at 64Mhz.
I compile using Mikro PIC32 Pascal Compiler.

So would appreciate any suggestions as to what to try to erase the display using a Mikro TFT command .

Thanks
Attachments
Both Components on screen
Both Components on screen
TFT KD024FM Jan2014 005.jpg (52.34 KiB) Viewed 14615 times
MyScreen1LabelComponents
MyScreen1LabelComponents
TFT KD024FM Jan2014 001.jpg (42.16 KiB) Viewed 14615 times
My5ButtonComponents
My5ButtonComponents
TFT KD024FM Jan2014 003.jpg (39.01 KiB) Viewed 14615 times
Rotary_Ed
Matthews, NC USA
Rv-6A N494BW Rotary Powered

aCkO
Posts: 1119
Joined: 14 Feb 2011 04:07
Location: Bar, Montenegro

Re: [Tutorial] Reusable objects on multiple screens

#9 Post by aCkO » 02 Jan 2014 08:21

Hi Ed,

You certainly went through a lot of trouble to end up with such a cumbersome code. If you don't need touch panel functionality then you don't have to follow my tutorial. In fact, the solution then becomes much simpler. Here's what you need to do after you generate the code for one screen in VTFT:

1. Delete all code related to touch panel (pin declarations, initialization etc.)
2. Delete all functions/procedures except the ones needed for drawing the components you are using (DrawLabel and DrawRoundButton)
3. To draw the screen use this code:

Code: Select all

procedure DrawScreen();
begin

  // Draw Background
  TFT_Fill_Screen(CL_BLUE);

  // Draw Buttons
  DrawRoundButton(@ButtonRound1);
  DrawRoundButton(@ButtonRound2);
  DrawRoundButton(@ButtonRound3);
  DrawRoundButton(@ButtonRound4);
  DrawRoundButton(@ButtonRound5);
  
  // Draw Labels
  DrawLabel(@Label1);
  DrawLabel(@Label2);
  DrawLabel(@Label3);
  DrawLabel(@Label4);
  DrawLabel(@Label5);
end;
And that's it. You don't even need multiple screens. Just change the label captions and redraw the screen.

I am attaching 2 versions of the project. The first one (simple) redraws the whole screen after changing label captions which produces the flickering effect (the above code).
The second one (advanced) uses a technique of clearing the labels by drawing the same caption with background color. That means the background and the buttons will be drawn only once. I have used block comments (curly braces) to comment out unnecessary code so the changes I have made can be easier to spot.

Regards
Attachments
No_Touch_Demo.zip
(23.39 KiB) Downloaded 482 times

Rotary_Ed
Posts: 756
Joined: 26 Dec 2004 23:10
Location: Matthews, NC, USA
Contact:

Re: [Tutorial] Reusable objects on multiple screens

#10 Post by Rotary_Ed » 02 Jan 2014 15:14

Thanks, ack0

Can only agree with your assessment of my attempt. I always seem to look for and find the hard way to do something in code. :? As almost always, the best code is simply and elegant, I appreciate you taking the time to show a much better way.

I popped the sample of the code you put in your message and it works just fine, I can now clear the screen and draw new screens with ease :D .

I've downloaded the code you provided and will take a closer look later.

Happy New Year
Rotary_Ed
Matthews, NC USA
Rv-6A N494BW Rotary Powered

Post Reply

Return to “Visual TFT General”