пʼятницю, 15 листопада 2019 р.

STEmWin: GUIBuilder приклад побудови вікна з графічними елементами і їх взаємодія


Передмова

В попередній статті інтегрували професійну бібліотеку EmWin до свого проекту та спробували щось відобразити на екрані і навіть помалювати за допомоги сенсорної панелі. В цій статті побудуємо, за допомоги програми GUIBuilder, графічний інтерфейс та додамо код взаємодії з графічним інтерфейсом.

Залізяччя

GUI Builder

Відкрийте програму "GUIBuilder", яка знаходиться в репозиторію CubeMX, або CubeIDE за шляхом "...\Middlewares\ST\STemWin\Software\GUIBuilder.exe".

Window

  1. Додайте елемент "Window";
  2. Встановіть властивості вікна: xPos - 0, yPos - 0, xSize - 240, ySize - 320;
  3. Встановимо колір вікна. ПКМ на елементі "Window", та оберіть "Set background color" -> Сірий (0xC0C0C0).
Window

Text

  1. Додайте елемент "Text";
  2. Встановимо позицію та розмір елементу. xPos - 0, yPos - 0, xSize - 240, ySize - 40;
  3. Встановимо шрифт. ПКМ на елементі "Text" -> "Set font" -> "GUI_FONT_24_1";
  4. Встановимо текст. ПКМ на елементі "Text" -> "Set text" -> "Example";
  5. Встановимо рівняння тексту. ПКМ на елементі "Text" -> "Set text alignment" -> "HV";
Text

Radio

  1. Додайте елемент "Radio";
  2. Встановимо позицію та розмір елементу: xPos - 20, yPos - 60, xSize - 200, ySize - 60, distance - 30;
  3. Дамо назву пунктам. ПКМ на елементі "Radio" -> "Set item text" -> "Label text" -> "Option 1" -> "Label index" -> "1", "Radio" -> "Set item text" -> "Label text" -> "Option 2" -> "Label index" -> "2";
  4. Встановимо шрифт: Font - GUI_FONT_20_1;
Radio

Progbar та 2 x Button

По аналогії з попередніми елементами додайте елементи "Progbar" та "Button". Розташуйте, встановіть розмір, оберіть шрифт, дайте назви кнопкам. Щоб було приблизно як на малюнку.
Приклад інтерфейсу

Генерування проекту

Меню: "File" -> "Save...". Файл "WindowDLG.c" збережеться в директорію програми "GUIBuilder" за шляхом "...\Middlewares\ST\STemWin\Software\WindowDLG.c".

Перевірка роботи

Продовжимо працювати з проектом який створили в попередній статті "Підключення бібліотеки EmWin та перевірка роботи"
    1. Відкрийте проект створений в процесі роботи з попередньою статтею, який знаходиться на вашому диску, та відкрийте його в Atolic TrueStudio, CubeIDE, або в IDE, яку ви використовуєте; 
    2. Створіть файл "WindowDLG.h" в своєму засобі розробки, або будь яким іншим редактором, з таким змістом:
      #ifndef __WINDOWDLG_H
      #define __WINDOWDLG_H
      
      #include "DIALOG.h"
      
      WM_HWIN CreateWindow(void);
      
      #endif /* __WINDOWDLG_H */
      
    3. Внесіть зміни до файлу "WindowDLG.c". Було:
      // USER START (Optionally insert additional includes)
      // USER END
      
      #include "DIALOG.h"
      
    4. Стало:
      // USER START (Optionally insert additional includes)
      // USER END
      
      #include "WindowDLG.h"
      
    5. Зкопіюйте ці файли, "WindowDLG.h" та "WindowDLG.с" в свій проект за шляхом "..\Drivers\EmWin\inc" та "..\Drivers\EmWin\src" відповідно;
    6. Відкрийте файл "main.c" та вставте в код рядок з файлом "WindowDLG.h":
      /* Private includes ----------------------------------------------------------*/
      /* USER CODE BEGIN Includes */
      #include "ili9341_light.h"
      #include "GUI.h"
      #include "XPT2046_touch.h"
      #include "WindowDLG.h"
      /* USER CODE END Includes */
    7. В користувацькій секції 2 і безкінечному циклі видаліть код, який написали з попередньої статті. Тепер ця ділянка має виглядати так:
      /* USER CODE BEGIN 2 */
        GUI_Init();
        lcdBacklightOn();
        CreateWindow();
        /* USER CODE END 2 */
      
        /* Infinite loop */
        /* USER CODE BEGIN WHILE */
        while (1)
        {
           GUI_Exec();
          /* USER CODE END WHILE */
      
          /* USER CODE BEGIN 3 */
        }
        /* USER CODE END 3 */
      }
    8. Можна вже компілювати, як немає помилок, заливаємо до чипу і запускаємо. На дисплеї з'явилося зображення, що ми створили за допомогою програми "GUIBuilder.exe". Але поки що наш GUI ніяк не реагує на натискання сенсорної панелі.
    9. Додамо декілька глобальних змінних до файлу "main.c", це добуті координати від сенсорної панелі і структура для координат і статусу натискання на сенсорну панель для GUI:
      /* USER CODE BEGIN PV */
      uint16_t x, y;
      GUI_PID_STATE tsState;
      /* USER CODE END PV */
      
    10. Перейдемо до користувацької секції 4. Там має бути функція зворотного виклику, яка викликається кожного разу коли ви натискаєте на сенсорну панель, та відпускаєте її. За це відповідає сигнал T_IRQ. Докладно налаштування цієї шпильки описано в статті. Перевірте, щоб переривання цієї ніжки відбувалось, як при падінні сигналу, так і при зростанні сигналу на ніжці - GPIO mode -> External Interrupt Mode with Rising/Falling edge trigger detection. Видаляємо весь код який знаходиться в функції зворотного виклику void HAL_GPIO_EXTI_Callback (uint16_t GPIO_Pin) і залишився з попереднього прикладу, та пишемо там такий код:
      /* USER CODE BEGIN 4 */
      void HAL_GPIO_EXTI_Callback (uint16_t GPIO_Pin)
      {
       if (GPIO_Pin == T_IRQ_Pin)
       {
        if(XPT2046_TouchPressed())
        {
         tsState.Pressed = true;
      
         if(XPT2046_TouchGetCoordinates(&x, &y))
         {
           tsState.x = x;
           tsState.y = GUI_GetScreenSizeY() - y;
         }
        }
        else
        {
         tsState.Pressed = false;
         tsState.x = -1;
         tsState.y = -1;
        }
      
        GUI_TOUCH_StoreStateEx(&tsState);
       }
      }
      /* USER CODE END 4 */
      
    11. Пояснення до коду. Кожного разу коли ви натискаєте чи відпускаєте сенсорну панель, викликається ця функція. Тут перевіряється чи відбулось натискання, або звільнення сенсорної панелі. Як натиснули, то берем координати точки натискання і заповнюємо структуру координатами і встановлюємо прапорець "натиснуто". Як звільнили сенсорну панель, то заповнюємо структуру відповідно, що координат немає і скидаємо прапорець "натиснуто". Заносимо данні з структури до GUI_TOUCH.
    12. Компілюємо, заливаємо, запускаємо. Тепер наш GUI реагує на натискання кнопок і можемо обрати опцію в елементі "Radio", але при цьому ще нічого не відбувається.
    13. Щоб додати реакцію на події коли натискати на опції елементу "Radio", відкриваємо файл "WindowDLG.c", знайдіть рядок "case ID_RADIO_0: // Notifications sent by 'Radio'", та додамо в секцію "case WM_NOTIFICATION_RELEASED:" код який буде оброблятись коли ви будете змінювати опцію в елементі "Radio" при відпусканні:
      case ID_RADIO_0: // Notifications sent by 'Radio'
            switch(NCode) {
            case WM_NOTIFICATION_CLICKED:
              // USER START (Optionally insert code for reacting on notification message)
              // USER END
              break;
            case WM_NOTIFICATION_RELEASED:
              // USER START (Optionally insert code for reacting on notification message)
               {
                char buf[32];
                hItem = WM_GetDialogItem(pMsg->hWin, ID_RADIO_0);
                RADIO_GetText(hItem, RADIO_GetValue(hItem), buf, 32);
                hItem = WM_GetDialogItem(pMsg->hWin, ID_TEXT_0);
                TEXT_SetText(hItem, buf);
               }
              // USER END
              break;
            case WM_NOTIFICATION_VALUE_CHANGED:
              // USER START (Optionally insert code for reacting on notification message)
              // USER END
              break;
            // USER START (Optionally insert additional code for further notification handling)
            // USER END
            }
            break;
      
    14. Компілюємо, заливаємо, запускаємо. Тицяємо на опції "Radio" і текст елементу "Text" змінюється відповідно до змін.
    15. По аналогії додамо реакцію на натискання кнопок. Будемо змінювати значення елементу "Progbar", та міняти напис на елементі "Text". Спочатку додамо змінну "i", що буде зберігати значення для "Progbar", на початку файлу "WindowDLG.c":
      /*********************************************************************
      *
      *       Static data
      *
      **********************************************************************
      */
      
      // USER START (Optionally insert additional static data)
      int i;
      // USER END
      
    16. Та напишемо обробник події відпускання кнопок:
          case ID_BUTTON_0: // Notifications sent by 'Button'
            switch(NCode) {
            case WM_NOTIFICATION_CLICKED:
              // USER START (Optionally insert code for reacting on notification message)
              // USER END
              break;
            case WM_NOTIFICATION_RELEASED:
              // USER START (Optionally insert code for reacting on notification message)
         {
          char buf[32];
          hItem = WM_GetDialogItem(pMsg->hWin, ID_BUTTON_0);
          BUTTON_GetText(hItem, buf, 32);
          hItem = WM_GetDialogItem(pMsg->hWin, ID_TEXT_0);
          TEXT_SetText(hItem, buf);
          if(i < 100)
           ++i;
          hItem = WM_GetDialogItem(pMsg->hWin, ID_PROGBAR_0);
          PROGBAR_SetValue(hItem, i);
         }
              // USER END
              break;
            // USER START (Optionally insert additional code for further notification handling)
            // USER END
            }
            break;
          case ID_BUTTON_1: // Notifications sent by 'Button'
            switch(NCode) {
            case WM_NOTIFICATION_CLICKED:
              // USER START (Optionally insert code for reacting on notification message)
              // USER END
              break;
            case WM_NOTIFICATION_RELEASED:
              // USER START (Optionally insert code for reacting on notification message)
            {
           char buf[32];
        hItem = WM_GetDialogItem(pMsg->hWin, ID_BUTTON_1);
        BUTTON_GetText(hItem, buf, 32);
        hItem = WM_GetDialogItem(pMsg->hWin, ID_TEXT_0);
        TEXT_SetText(hItem, buf);
        if(i > 0)
          --i;
        hItem = WM_GetDialogItem(pMsg->hWin, ID_PROGBAR_0);
        PROGBAR_SetValue(hItem, i);
            }
              // USER END
              break;
            // USER START (Optionally insert additional code for further notification handling)
            // USER END
            }
            break;
          // USER START (Optionally insert additional code for further Ids)
          // USER END
          }
          break;
        // USER START (Optionally insert additional message handling)
        // USER END
      
    17. Компілюємо, заливаємо, запускаємо. Тицяємо на елементи, спостерігаємо за реакцією.

    Відео приклад роботи та практичні уроки





    11 коментарів:

    1. Дякую за статтю. Вроді розібрався

      ВідповістиВидалити
    2. Будь ласка, використовуйте CUT! Дякую за матеріал і розуміння.

      ВідповістиВидалити
    3. Дякую за чудову статтю! Все робив під Linux і без IDE, дуже допомогла ваша стаття! Побільше б таких...

      ВідповістиВидалити
    4. А вы пробовали нажимать на виджеты с помощью внешних физических кнопок?

      ВідповістиВидалити
    5. Дякую, гарна iнфо. Але в мене щось не працюе на стадii створення змiнних WM_HWIN maineW; в файлi Myinterface.h. Пише помилку при компiляцii - Core/Inc/Myinterface.h:16: first defined here. Може iх ще десь декларувати потрiбно?

      ВідповістиВидалити
      Відповіді
      1. Напишiть, будь ласка на fisher196@meta.ua

        Видалити
      2. Перегляньте 6 відео, там на початку виправлення.

        Видалити
      3. Так, до шостого видео я не дiйшов. Дякую. Але маякнiть менi на мило, будь ласка, е ще питання.

        Видалити