Jump to content

Information menu


Alan Chang

Recommended Posts

Hello,

I want to use a small area of the screen to show the information menu.

User can switch the different information by a hardware button.

Is it a good way to use "console"?

Here is my "console" code.

GHandle ghMode1;

void Mode1Infom(void)
{
	GWindowInit		wi1;
	gwinClearInit(&wi1);

	// create console windows: ghMode1
	wi1.show = TRUE;
	wi1.x = 0;
	wi1.y = 140;
	wi1.width = 240;
	wi1.height = 24;
	ghMode1 = gwinConsoleCreate(0, &wi1);
	gwinSetFont(ghMode1, font1);
	gwinSetColor(ghMode1, Green);
	gwinSetBgColor(ghMode1, Red);
}

void guiShowMode(unsigned pageIndex)
{
gwinClear(ghMode1);

switch (pageIndex) {
	case 1:
		gwinPrintf(ghMode1, "Hello1 123\n");
	break;
	
	case 2:
		gwinPrintf(ghMode1, "Hello2 456\n");
	break;
}

	

Then attach toogle.

gwinAttachToggle(ghMode1, 0, 0);

 

Is this code OK?

 

Also what is the event type for "console"?

 

Please give me some suggestion.

Thanks.

 

 

 

Link to comment
Share on other sites

Using the console widget is definitely a bad choice in any case. The console widget is not really meant for text that changes, labels are more suited for that. The console widget doesn't generate any events (in developer terms it's a window, not a widget).

For this kind of thing you usually want to implement a custom widget (unless a label is enough, which it seems to be the case here so far).

Link to comment
Share on other sites

Hi Joel,

 

I am creating my custom widget.

When I make a gwidgetVMT, I can not understand something.

How does the widget get the toogle (hardware button) status?

 

I refer to the gwin_button.c.

I found "ButtonToggleOn".

static void ButtonToggleOn(GWidgetObject *gw, uint16_t role) {
		(void) role;
		
		gw->g.flags |= GBUTTON_FLG_PRESSED;
		
		_gwinUpdate((GHandle)gw);
		// Trigger the event on button down (different than for mouse/touch)
		_gwinSendEvent(&gw->g, GEVENT_GWIN_BUTTON);
	}

 

Is the toggle status saved in "gw->g.flags"?

And is this status from  "ginputGetToggle()" in the ginput_toggle.c file?

 

Hope you can understand my question.

Thanks.

Link to comment
Share on other sites

Hello Alan,

The gwidgetVMT allows you to register functions for the following toggle events (copied from the documentation):

uint16_t                toggleroles;                                                        /**< The roles supported for toggles (0->toggleroles-1) */
void (*ToggleAssign)    (GWidgetObject *gw, uint16_t role, uint16_t instance);              /**< Assign a toggle to a role (optional) */
uint16_t (*ToggleGet)   (GWidgetObject *gw, uint16_t role);                                 /**< Return the instance for a particular role (optional) */
void (*ToggleOff)       (GWidgetObject *gw, uint16_t role);                                 /**< Process toggle off events (optional) */
void (*ToggleOn)        (GWidgetObject *gw, uint16_t role);                                 /**< Process toggle on events (optional) */

The toggle status is not saved in gw->g.flags. A toggle can have two states: on and off (true or false, 1 or 0, ...). You get a call to ToggleOn() and ToggleOff() accordingly. It's up to you as a developer to decide what to do with that information afterwards. In case of the existing pushbutton widget we just change the internal flag of the pushbutton that keeps track of the pressed/unpressed state. But in your widget you do whatever you think is best. ToggleOn() and ToggleOff() just inform you that the state of the toggle (the hardware button) changed. The rest is up to you.

Link to comment
Share on other sites

Hello Joel,

 

Thanks for your information.

I think my custom widget has some issue and can not be attached well with gwinAttachToggle().

There is no response when executed, so I traced the code.

I found this part is always be "TRUE".

bool_t gwinAttachToggle(GHandle gh, uint16_t role, uint16_t instance) {

...

if (instance == oi)
	return TRUE;

...
}

 

However, I tried to create a win button(gwinButtonCreate()), and check the oi value.

It is always have a value which is 65535.

I think the value of oi is given here.

oi = wvmt->ToggleGet(gw, role);

 

static uint16_t ButtonToggleGet(GWidgetObject *gw, uint16_t role) {
		(void) role;
		return ((GButtonObject *)gw)->toggle;
	}

When is the value put in "((GButtonObject *)gw)->toggle" ?

 

Here is my widget VMT.

static const gwidgetVMT modeVMT = {
	{
		//gwindowVMT g part start------
		"Mode",						// The classname
		sizeof(ModeObject),			// The object size
		_gwidgetDestroy,			// The destroy routine
		_gwidgetRedraw,				// The redraw routine
		0,							// The after-clear routine
	},
		//gwindowVMT g part end------
	
	modeDraw_Default,				// The default drawing routine
	#if GINPUT_NEED_MOUSE
		{
			0,				// Process mouse down events
			0,						// Process mouse up events
			0,						// Process mouse move events
		},
	#endif
	#if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD
		{
			0						// Process keyboard events
		},
	#endif
	#if GINPUT_NEED_TOGGLE
		{
			1,						// Toggle role
			uModeToggleAssign,						// Assign Toggles
			uModeToggleGet,						// Get Toggles
			0,						// Process toggle off events
			uModeToggleOn,						// Process toggle on events
		},
	#endif
	#if GINPUT_NEED_DIAL
		{
			0,						// No dial roles
			0,						// Assign Dials
			0,						// Get Dials
			0,						// Process dial move events
		},
	#endif
};

and 

static void uModeToggleAssign(GWidgetObject *gw, uint16_t role, uint16_t instance) {
		(void) role;
		((ModeObject *)gw)->toggle = instance;
	
	}


static uint16_t uModeToggleGet(GWidgetObject *gw, uint16_t role) {
		(void) role;	
		return ((ModeObject *)gw)->toggle;
	}

 

 

Is anything wrong in my code?

Please help.

Thanks.

 

Link to comment
Share on other sites

Hello,

 

I used gwinAttachToggle to assign the toggle for my widget.

When the hardware button is pressed, the listener will receive an event and call "the page change" function.

I use the geventEventWait to wait the event.

pe = geventEventWait(&_glistener, TIME_INFINITE);

However, this function only keeps waiting an event.

 

If I do not press the button, I want the information of current page can continue update.  

Also wait for the event at same.

What can I do in my code?

Thanks.

Link to comment
Share on other sites

Hello Alan,

I'm not sure whether you want a custom widget to receive & handle the toggle or whether you just want a generic toggle event in your applications event processing loop. Can you please clarify this?

Did you properly use gwinAttachToggle() to attach the toggle to your custom widget? Did you check for the return value of that function? It returns FALSE if attaching failed.
Note that pressing the toggle will not generate an event that you receive through the regular geventEventWait() setup. Your widget's toggle handling functions will be called. You can process the toggle event there and if you like you can create an event there.

Did you make sure that your toggle board file is correct? I'd recommend you to properly debug this by setting breakpoints in the corresponding functions.

Link to comment
Share on other sites

The 2nd parameter to geventEventWait is a timeout. You can specify a timeout in milliseconds for geventEventWait to wait for an event. If it hasn't found an event during that time it returns NULL rather than the event pointer.

By placing geventEventWait in a loop with a suitable timeout period, and by handling the NULL that can now be returned, you can now do other things as well in the loop.

Link to comment
Share on other sites

Another way is to create a separate thread to run other tasks; or put geventEventWait into the new thread and use the main thread to do your other tasks.

If you use this thread approach and are using a cooperative schedular (like in the RAW32 or Arduino ports) don't forget to run gfxYield periodically to give other threads a chance to run.

Link to comment
Share on other sites

Hi guys ,

 

Very thanks for your information.

My application will be similar like this video.

https://www.youtube.com/watch?v=9dCVJIUPspM 

 

I designed my custom widget for these two things below. 

1. Change pages by a hardware button.

2. Show the latest information in every page. So it will be always updates the data.

 

I tested the  gwinAttachToggle()  and there was no FALSE  returned.

The pages can be changed.

So I think toggle is working with my custom widget well.

 

Now if I do not change page, the information of this present page still need to update all the time.

Just when I tried to update the information, the loop was blocked in geventEventWait.

It was waiting for the toggle event.

However, I think I can not set a specific time in geventEventWait.

Because I do not know when the user press the hardware button, and some information like clock and clock can be really changed very quickly.

Is there any return value of some API which I can handle during the geventEventWait loop?

Or I only can solve by create a new thread.

Hope you can give me some suggestion.

By the way, I am using FreeRTOS.

 

Thank you.

 

 

 

 

Link to comment
Share on other sites

Hello Alan,

For things like this, you can use a software timer. The GTIMER module provides those. They are extremely simple to use: Write your function, create a GTIMER object and specify the interval --> https://wiki.ugfx.io/index.php/GTIMER

Software timers are less flexible and less precise than threads. However, for things like updating the UI they are usually more than sufficient. If you feel like that's not the case, then go straight for a dedicated thread. There are demos on how to create threads with the µGFX API (gfxThreadCreate()). µGFX will directly use the underlying threads provided by FreeRTOS in your case.

Link to comment
Share on other sites

Hi Joel,

 

Can I build two "gtimerStart" functions at same time?

Like this.

gtimerStart(&gtimer_Demo, auto_Demo, 0, TRUE, AUTO_DEMO_INTERVAL);
gtimerStart(&gtimer_UpdateItem, updateModeItem, 0, TRUE, UPDATE_ITEM_INTERVAL);

 

Now when I executed this program, the second one did not work.

 

Thanks.

 

Link to comment
Share on other sites

Hello Alan,

Yes, you can have as many running GTimers as you want (it's fine to have two gtimerStart()). However, don't forget to call gtimerInit() on each GTimer object. It should look like this:

GTimer gtimer_Demo;
GTimer gtimer_UpdateItem;

gtimerInit(&gtimer_Demo);
gtimerInit(&gtimer_UpdateItem);

gtimerStart(&gtimer_Demo, auto_Demo, 0, TRUE, AUTO_DEMO_INTERVAL);
gtimerStart(&gtimer_UpdateItem, updateModeItem, 0, TRUE, UPDATE_ITEM_INTERVAL);

Did you do that?

If this was not the problem: How did you check that the second one didn't work?

Keep in mind that unlike threads, all GTimers are executed in one thread (the thread internally created & maintained by the GTIMER module). Each GTimer instance will run on the same stack and the same priority. So make sure that GTIMER_THREAD_WORKAREA_SIZE is set accordingly.

Link to comment
Share on other sites

Hi Joel,

 

Thanks for your quick reply.

And Yes, I did the function code same as yours below when used the gtimer.

GTimer gtimer_Demo;
GTimer gtimer_UpdateItem;

gtimerInit(&gtimer_Demo);
gtimerInit(&gtimer_UpdateItem);

gtimerStart(&gtimer_Demo, auto_Demo, 0, TRUE, AUTO_DEMO_INTERVAL);
gtimerStart(&gtimer_UpdateItem, updateModeItem, 0, TRUE, UPDATE_ITEM_INTERVAL);

 

How did you check that the second one didn't work? ==> I used "printf()" in the call back function to output a simple message on PuTTY for debug. And there is no output in second "gtimerStart()".

 In gfxconf.h the GTIMER configuration is below.

///////////////////////////////////////////////////////////////////////////
// GTIMER                                                                //
///////////////////////////////////////////////////////////////////////////
#define GFX_USE_GTIMER                               TRUE

#define GTIMER_THREAD_PRIORITY                       HIGH_PRIORITY
#define GTIMER_THREAD_WORKAREA_SIZE                  2048

Is there any issue?

 

Thanks.

Link to comment
Share on other sites

Hi Joel,

The autoDemo() function actually is from the part of the dial widget example.

And I add the digital number ICON display function which is display3DigitalNumber().

 

The  updateModeItem() is for update the clock.

void autoDemo(void)
{
	uint16_t dialAngle = dialGetAngle(ghDial);

	printf("456");  //alan test
	display3DigitalNumber(150);
	gfxSleepMilliseconds(80);
	display3DigitalNumber(151);
	gfxSleepMilliseconds(80);

	// Increment the dial position and handle overflows
	if (dialAngle >= 180) {
		dialAngle = 0;
	} else {
		dialAngle++;
	}

	// Apply the new dial needle position
	dialSetAngle(ghDial, dialAngle);
}

void updateModeItem(void)
{
	printf("123");  //alan test	
	modeClockUpdate();											//update clock

}

Then I used gtimeStart() to call them.

gtimerStart(&gtimer_Demo, auto_Demo, 0, TRUE, AUTO_DEMO_INTERVAL);
gtimerStart(&gtimer_UpdateItem, updateModeItem, 0, TRUE, UPDATE_ITEM_INTERVAL);

The video of the result is blow.

MOV_0365.mp4

 

Today I found this.

If I deleted the code in the autoDemo().

	//display3DigitalNumber(150);
	//gfxSleepMilliseconds(80);
	//display3DigitalNumber(151);
	//gfxSleepMilliseconds(80);

Every thing works well.

MOV_0369.mp4

 

 The display3DigitalNumber() is built by the gdispImageOpenFile(), gdispImageDraw() and gdispImageClose().

Such as this.

switch (number){
	case 0:
		gdispImageOpenFile(&_Digtal_num0, "rsc/P0_48x68.bmp"); 
		gdispImageDraw(&_Digtal_num0, p_x, p_y, gdispGetWidth(),gdispGetHeight(), 0, 0); //show image file
		gdispImageClose(&_Digtal_num0);	
		break;
	case 1:
		gdispImageOpenFile(&_Digtal_num1, "rsc/P1_48x68.bmp"); 
		gdispImageDraw(&_Digtal_num1, p_x, p_y, gdispGetWidth(),gdispGetHeight(), 0, 0); //show image file
		gdispImageClose(&_Digtal_num1);	
		break;

 

Will it delay the timer when open the image file?

How can I solve this?

 

By the way, the  UPDATE_ITEM_INTERVAL means the interal time to update the clock for my custom widget.

I set 100 here.

 

Thanks.

 

 

Link to comment
Share on other sites

gtimer runs on its own thread. This means that if one timer job takes a long time then that will definitely delay other timer jobs that are ready to run. For a cooperative os like raw32 the same also applies to work done in any other thread.

The key point here however is that the delay caused is only after a task is ready to run. It doesn't delay when the task is ready to run. E.g... 

A timer task at 35000ms system time is set to run in 100ms. This means the task is "ready" to run at 35100ms system time. No matter what else is happening in other tasks that doesn't change. The timer task will then be run at the first available opportunity after 35100ms system time. Having other busy tasks will only delay that first opportunity. 

The advantage of this is if you set a repeating timer then the cycle rate will average that period even if a specific timer event is delayed due to the cpu being busy doing other things. 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...