Lv_scale animations with C++ class

Description

I am trying to implement round scale with needle-line (aka Speedometer) inside C++ class and function that run animation of moving needle to some value. When i am passing class object pointer to callback function, that invokes lv_scale_set_line_needle_value, my data corrupts and lv_scale_set_line_needle_value retruns nothing becourse scale->mode is random numbers (should be 8)

What MCU/Processor/Board and compiler are you using?

G++5 Linux Ubuntu 16.04 SDL simulator

What LVGL version are you using?

9.1 master

What do you want to achieve?

Class function called by class object run animation of moving needle line to set value.

What have you tried so far?

  1. Lambda for lv_anim_set_custom_exec_cb / lv_anim_set_exec_cb
  2. Passing objects with lv_anim user_data
    3.Passing objects with void * obj to callack function

Code to reproduce


/**
 * @file main
 *
 */

/*********************
 *      INCLUDES
 *********************/
#define _DEFAULT_SOURCE /* needed for usleep() */
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "lvgl/lvgl.h"
//#include "lvgl/examples/lv_examples.h"
#include "lvgl/demos/lv_demos.h"
#include "main.h"

#include <sys/time.h>
#include <stdint.h>


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include <signal.h>


#include <functional>


/*********************
 *      DEFINES
 *********************/
pthread_mutex_t lock;
timer_t timer_id;

// Обработчик сигнала таймера
void timer_handler(int signum) {
  //pthread_mutex_lock(&lock);
    // Увеличить таймер LVGL
    lv_tick_inc(5);
   // pthread_mutex_unlock(&lock);

}

// Создать и запустить таймер LVGL
void lvgl_timer_init() {
    // Создать таймер
    struct sigevent sev;
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = SIGRTMIN;
    sev.sigev_value.sival_ptr = &timer_id;
    timer_create(CLOCK_REALTIME, &sev, &timer_id);

    // Установить интервал таймера
    struct itimerspec its;
    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 5 * 1000 * 1000;  // 5 миллисекунд
    its.it_value.tv_sec = 0;
    its.it_value.tv_nsec = 5 * 1000 * 1000;  // 5 миллисекунд
    timer_settime(timer_id, 0, &its, NULL);

    // Установить обработчик сигнала таймера
    signal(SIGRTMIN, timer_handler);
}
/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static lv_display_t * hal_init(int32_t w, int32_t h);

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *      VARIABLES
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/



class Meter {
public:
    Meter(lv_obj_t * parent, const char * title, const char * text1, int pos_x, int pos_y, int min_value, int max_value, int start, int end);
    ~Meter();
    static void setValue(lv_anim_t * obj, int32_t val);
    static void anim_ready_cb(lv_anim_t * a);
    void runAnim(int32_t val);
    void getScale() {this->scale;};
    void getNeedle() {this->needle_line;};

    int32_t value = 0;
    int32_t ticks_count = 40 + 1;
    lv_obj_t * scale;
    lv_obj_t * needle_line;
    lv_anim_t * anim_scale_line2;
    void invalidateScale() {lv_obj_invalidate(this->needle_line);};
    void sett(int32_t val);
private:

};

Meter::Meter(lv_obj_t * parent, const char * title, const char * text1, int pos_x, int pos_y, int min_value, int max_value, int start, int end) {
    printf("Creating meter!\n");
    int32_t ticks_count = 40 + 1;
    lv_obj_t * scale = lv_scale_create(parent);
    lv_scale_set_mode(scale, LV_SCALE_MODE_ROUND_INNER);
    lv_scale_set_post_draw(scale, true);
    lv_obj_set_width(scale, LV_PCT(100));
    lv_obj_set_pos(scale, pos_x, pos_y);
    lv_scale_set_angle_range(scale, 220);
    lv_scale_set_rotation(scale, 160);
    lv_obj_set_size(scale, 150, 150);
    lv_scale_set_range(scale, min_value, max_value);
    lv_scale_set_total_tick_count(scale, ticks_count);
    lv_scale_set_major_tick_every(scale, (ticks_count / 4)); // big ticks count - 1
    lv_scale_set_label_show(scale, true);
    lv_obj_set_style_radius(scale, LV_RADIUS_CIRCLE, 0);
    lv_obj_set_style_length(scale, 10, LV_PART_INDICATOR | LV_STATE_DEFAULT);
    lv_obj_set_style_length(scale, 100, LV_PART_MAIN | LV_STATE_DEFAULT);
    lv_obj_set_style_length(scale, 5, LV_PART_ITEMS | LV_STATE_DEFAULT);
    lv_obj_t * needle_line = lv_line_create(scale);
    lv_obj_set_style_line_width(needle_line, 6, LV_PART_MAIN);
    lv_obj_set_style_line_rounded(needle_line, true, LV_PART_MAIN);
    lv_obj_set_pos(needle_line, 0, 0);
    lv_scale_set_line_needle_value(scale, needle_line, 60, value);

    lv_obj_t * text = lv_label_create(scale);
    lv_obj_set_pos(text, 0, 102);
    lv_obj_set_size(text, LV_PCT(100), LV_PCT(30));
    lv_label_set_text(text, text1);
    lv_obj_set_style_bg_color(text, lv_color_hex(0xff000000), LV_PART_MAIN | LV_STATE_DEFAULT);
    lv_obj_set_style_text_color(text, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
    lv_obj_set_style_bg_opa(text, LV_OPA_MAX, LV_PART_MAIN | LV_STATE_DEFAULT);
    lv_obj_set_style_text_align(text, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
    printf("global %d\n", value);



}
void Meter::setValue(lv_anim_t * obj, int32_t val) {
      printf("Executing animation  %d\n", val);
      Meter *self = static_cast<Meter *>(lv_anim_get_user_data(obj));
      lv_scale_t *j = (lv_scale_t*)(self->scale);
      lv_obj_t *scale = static_cast<lv_obj_t *>(lv_anim_get_user_data(obj));
      lv_scale_set_line_needle_value((static_cast<Meter *>(lv_anim_get_user_data(obj)))->scale, (static_cast<Meter *>(lv_anim_get_user_data(obj)))->needle_line, 60, val);

      self->value = val;

}



void Meter::runAnim(int32_t val) {
  printf("Prepearing animation!\n");
     lv_anim_t anim_scale_line2;
    lv_anim_init(&anim_scale_line2);
    lv_anim_set_var(&anim_scale_line2, this->scale);
    lv_anim_set_completed_cb(&anim_scale_line2, anim_ready_cb);
    lv_anim_set_user_data(&anim_scale_line2, this);
    lv_anim_set_custom_exec_cb(&anim_scale_line2, setValue);
    lv_anim_set_duration(&anim_scale_line2, 2000);
    lv_anim_set_repeat_count(&anim_scale_line2, 0);

    lv_anim_set_values(&anim_scale_line2, 0, val);
    lv_anim_start(&anim_scale_line2);
    printf("Scale pointer is %");
}

void Meter::anim_ready_cb(lv_anim_t * a) {
  printf("Animation Completed!\n");
  Meter  *value = static_cast<Meter *>(lv_anim_get_user_data(a));
  printf("global %d\n", value->value);
}
Meter::~Meter() {};


/**********************
 *   GLOBAL FUNCTIONS
 **********************/


int main(int argc, char **argv)
{


  (void)argc; /*Unused*/
  (void)argv; /*Unused*/
  //lv_tick_set_cb(get_milliseconds);
  /*Initialize LVGL*/
  lv_init();

  /*Initialize the HAL (display, input devices, tick) for LVGL*/
  hal_init(1024, 768);

  //lv_demo_widgets();
  //create_screen_main();
  //lv_example_scale_6();
  lvgl_timer_init();

Meter Meter1(lv_screen_active(), "1", "1", 140, 201, 0, 120, 90, 120);

Meter1.runAnim(80);

  while(1) {
    /* Periodically call the lv_task handler.
     * It could be done in a timer interrupt or an OS task too.*/

    lv_refr_now(NULL);
    usleep(5 * 1000);
    lv_timer_handler();
  }

  return 0;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

/**
 * Initialize the Hardware Abstraction Layer (HAL) for the LVGL graphics
 * library
 */



static lv_display_t * hal_init(int32_t w, int32_t h)
{

  lv_group_set_default(lv_group_create());

  lv_display_t * disp = lv_sdl_window_create(w, h);

  lv_indev_t * mouse = lv_sdl_mouse_create();
  lv_indev_set_group(mouse, lv_group_get_default());
  lv_indev_set_display(mouse, disp);
  lv_display_set_default(disp);


  lv_indev_t * mousewheel = lv_sdl_mousewheel_create();
  lv_indev_set_display(mousewheel, disp);
  lv_indev_set_group(mousewheel, lv_group_get_default());

  lv_indev_t * kb = lv_sdl_keyboard_create();
  lv_indev_set_display(kb, disp);
  lv_indev_set_group(kb, lv_group_get_default());

  return disp;
}


Screenshot and/or video

class Meter
{
  public:
    Meter(lv_obj_t * parent, const char * title, const char * text1, int pos_x, int pos_y, int min_value, int max_value, int start, int end);
    ~Meter();
    void set(int32_t val, lv_anim_enable_t anim_enable = LV_ANIM_ON);
    inline int32_t get()       { return this->value; }
  private:
    int32_t value = 0;
    lv_obj_t * scale;
};

Meter::Meter(lv_obj_t * parent, const char * title, const char * text1, int pos_x, int pos_y, int min_value, int max_value, int start, int end)
{
  printf("Creating meter!\n");
  int32_t ticks_count = 40 + 1;
  this->scale = lv_scale_create(parent);
  lv_scale_set_mode(scale, LV_SCALE_MODE_ROUND_INNER);
  lv_scale_set_post_draw(scale, true);
  lv_obj_set_width(scale, LV_PCT(100));
  lv_obj_set_pos(scale, pos_x, pos_y);
  lv_scale_set_angle_range(scale, 220);
  lv_scale_set_rotation(scale, 160);
  lv_obj_set_size(scale, 150, 150);
  lv_scale_set_range(scale, min_value, max_value);
  lv_scale_set_total_tick_count(scale, ticks_count);
  lv_scale_set_major_tick_every(scale, (ticks_count / 4)); // big ticks count - 1
  lv_scale_set_label_show(scale, true);
  lv_obj_set_style_radius(scale, LV_RADIUS_CIRCLE, 0);
  lv_obj_set_style_length(scale, 10, LV_PART_INDICATOR | LV_STATE_DEFAULT);
  lv_obj_set_style_length(scale, 100, LV_PART_MAIN | LV_STATE_DEFAULT);
  lv_obj_set_style_length(scale, 5, LV_PART_ITEMS | LV_STATE_DEFAULT);
  lv_obj_t * needle_line = lv_line_create(scale);
  lv_obj_set_style_line_width(needle_line, 6, LV_PART_MAIN);
  lv_obj_set_style_line_rounded(needle_line, true, LV_PART_MAIN);
  lv_obj_set_pos(needle_line, 0, 0);
  lv_scale_set_line_needle_value(scale, needle_line, 60, value);

  lv_obj_t * text = lv_label_create(scale);
  lv_obj_set_pos(text, 0, 102);
  lv_obj_set_size(text, LV_PCT(100), LV_PCT(30));
  lv_label_set_text(text, text1);
  lv_obj_set_style_bg_color(text, lv_color_hex(0xff000000), LV_PART_MAIN | LV_STATE_DEFAULT);
  lv_obj_set_style_text_color(text, lv_color_hex(0xffffffff), LV_PART_MAIN | LV_STATE_DEFAULT);
  lv_obj_set_style_bg_opa(text, LV_OPA_MAX, LV_PART_MAIN | LV_STATE_DEFAULT);
  lv_obj_set_style_text_align(text, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
  printf("global %d\n", value);

}

void Meter::set(int32_t val, lv_anim_enable_t anim_enable)
{
  if(anim_enable == LV_ANIM_OFF) {
    this->value = val;
    lv_obj_t * needle_line = lv_obj_get_child_by_type(this->scale, 0, &lv_line_class);
    lv_obj_t * text = lv_obj_get_child_by_type(this->scale, 0, &lv_label_class);
    lv_scale_set_line_needle_value(this->scale, needle_line, 60, this->value);
    lv_label_set_text_fmt(text, "%d", this->value);
  }
  else {
    lv_anim_t anim;
    lv_anim_init(&anim);
    lv_anim_set_var(&anim, this);
    lv_anim_set_custom_exec_cb(&anim, [](lv_anim_t * a, int32_t v) {
      Meter * meter = static_cast<Meter *>(a->var);
      meter->set(v, LV_ANIM_OFF);
    });
    lv_anim_set_completed_cb(&anim, [](lv_anim_t * a) {
      printf("Animation Completed!\n");
      Meter * meter = static_cast<Meter *>(a->var);
      printf("global %d\n", meter->get());
    });
    lv_anim_set_duration(&anim, 2000);
    lv_anim_set_repeat_count(&anim, 0);
    lv_anim_set_values(&anim, 0, val);
    lv_anim_start(&anim);
  }
}

Meter::~Meter() {};

Usage Example :

  Meter Meter1(lv_screen_active(), "1", "1", 140, 201, 0, 120, 90, 120);
  Meter1.set(80);                    // set value with ANIM
  // Meter1.set(80, LV_ANIM_OFF);    // set value without ANIM 

Thounsand thanks, sir! It works!