neu-rah / ArduinoMenu

Arduino generic menu/interactivity system
GNU Lesser General Public License v2.1
947 stars 191 forks source link

Idle InformationMenu question #100

Closed Redferne closed 7 years ago

Redferne commented 7 years ago

Hi,

This is more a question than issue. Is it possible to define an Idle "Screen" which is continuously updated? Some thing like this:

result Infomation(menuOut& o,idleEvent e) {
   if (e==idling) {
    o.setCursor(0,0);
    o.print("Time:");
    o.println(localtime);
  }
  return proceed;
}

So that the information submenu is called/updated with every call to nav.poll();

Thanks!

neu-rah commented 7 years ago

yes, some sort of... see:

https://github.com/neu-rah/ArduinoMenu/blob/master/src/menu.h#L521 https://github.com/neu-rah/ArduinoMenu/blob/master/src/menu.h#L583

however it will be called on poll if your device requires constant redraw otherwise only on entry and exit.

This can also be done outside the menu system, by not calling the menu poll and using the display for whatever purposes...

note: on system that do not require constant redraw it can be forced by invalidating the current draw (see force draw/update)

Redferne commented 7 years ago

I'm using the SSD1351 (Adafruit GFX) and it does not require constant redraw. I tried to define the display as menuOut::redraw and got the desired effect, the IdleScreen or "Device Monitor" updates for each call to nav.doOutput(). However this causes all menus to continuously update :-1: I could not find any reference to force draw/update, but there is a dirty bit? Please advice, it would be so nice to be able to define "live" panels where as its possible to continuously monitor certain parameters.

A general menu timeout would also be very nice, a callback function which would be called when there has been no input for x milliseconds.

I know this can be done outside the library but somehow it feels natural to have the design completely menu/input driven.

neu-rah commented 7 years ago

its is possible to monitor parameters (fields) as the menu will set the redraw flags whenever the values change (comparing to internal stored values).

https://github.com/neu-rah/ArduinoMenu/blob/master/src/menu.h#L144

Redferne commented 7 years ago

Yes. This works beautifully for the known datatypes, int, float. But not for char * "string". I managed to use the prompt class for custom data types. It prints the fix.dateTime once, but It does not redraw when changed?

class infoTime:public prompt {
public:
  infoTime(const promptShadow& p):prompt(p) {}
  idx_t printTo(navRoot &root,bool sel,menuOut& out, idx_t idx,idx_t len) override {
    root.active().dirty=true;  // <--- This does not work
    out << fix.dateTime;
    return 0;
  }
};

MENU(gpsMenu,"- GPS Info -",showEvent,anyEvent,noStyle
  ,altOP(infoTime, "", updateEvent,anyEvent)
  ,....
);

Change the text and force update:

gpsMenu.data[0]->text = "Updated Text";

But it does not compile. Please advice

neu-rah commented 7 years ago

nope, the printTo will only be called if menu has changed, so signalling dirty inside wont help... instead overload

virtual bool changed(const navNode &nav,const menuOut& out,bool sub=true) {return dirty;}

https://github.com/neu-rah/ArduinoMenu/blob/master/src/menu.h#L78

where you can always return true (can result in flicking or menu over usage) or use some timed thing to reduce the redraw rate, or even better detect when your information has changed to signal the draw needs.

Redferne commented 7 years ago

Some progress. Below class uses redraw_menu flag to signal dirty. But if I ever return false from the overridden changed(), prompt is not redrawn anymore. "Tick!" output is correct (once per second), however the display is not updated.

So basically I set redraw_menu = true every second, to get a new timestamp written to the display. nav.poll() is called every 100ms, my hope was that the below prompt would redraw every second and not cause to much CPU load. Please advice :cry:

class infoTime:public prompt {
public:
  infoTime(const promptShadow& p):prompt(p) {}
  bool changed(const navNode &nav,const menuOut& out,bool sub) override {
    if (redraw_menu) {
      Serial.println(F("Tick!"));
      redraw_menu = false;
      return true;
    }
    return false;
  }
  idx_t printTo(navRoot &root,bool sel,menuOut& out, idx_t idx,idx_t len) override {
    out.setColor(valColor,sel,enabled,true);
    PrintLocalTime(out, true);
    return 16;
  }
};
MENU(gpsMenu,">GPS Info<",doNothing,noEvent,wrapStyle
  ,altOP(infoTime, "", showEvent,anyEvent)
....
neu-rah commented 7 years ago

hi! this one seems to update when used on my serial example

int cnt=0;

class altPrompt:public prompt {
public:
  altPrompt(const promptShadow& p):prompt(p) {}
  idx_t printTo(navRoot &root,bool sel,menuOut& out, idx_t idx,idx_t len) override {
    return out.printRaw(String(cnt++).c_str(),len);
  }
  virtual bool changed(const navNode &nav,const menuOut& out,bool sub=true) {return true;}
};

p.s. i'm testing this on an esp (so all memory is ram, no flash) therefor this might not work on the uno or other avrs, just because i'm printing a ram string... so replace that with your own print stuff on avr's

Redferne commented 7 years ago

Yes. I've tried your solution, but problem is that the same line is redrawn continuously even if the string has not changed. I only want it to redraw when there is new data in the string or if I set a flag to trigger redraw as my example above (redraw_menu). Any ideas?

neu-rah commented 7 years ago

yes it does, you can compare with last drawn value and only signal changed when its updated... or if you have an updating function then signal that for changed

here's a thing i was loading on uno right now

//customizing a prompt look!
//by extending the prompt class
//this prompt will count seconds and update himself on the screen.
class altPrompt:public prompt {
public:
  unsigned int t=0;
  unsigned int last=0;
  altPrompt(const promptShadow& p):prompt(p) {}
  idx_t printTo(navRoot &root,bool sel,menuOut& out, idx_t idx,idx_t len) override {
    last=t;
    return out.printRaw(String(t).c_str(),len);
  }
  virtual bool changed(const navNode &nav,const menuOut& out,bool sub=true) {
    t=millis()/1000;
    return last!=t;
  }
};

this one has internal status and last drawn control to avoid constant redraw

Redferne commented 7 years ago

Wonderful! :grinning: It works perfectly. Thank you!