понедельник, 2 июля 2018 г.

00.01 Создание WxWidgets приложения.

Начиная с версии 3.0, библиотека WxWidgets разрабатывалась для использования совместно с юникодными строками.

1. Создаем пустой C++ проект. 
Запускаем MS VS 2022, выполняем: меню - File - New - Project - Windows Dectop Wizard - Windows Dectop Wizard.
Указываем имя проекта: WxEmpty, жмем кнопку ОК:





    



2. Настройка пустого проекта.
2.1 Настраиваем стандарт языка C++, кодировку строк и сбрасываем "бит консольности".
Во вкладке "Solution Explorer" выбираем проект WxEmpty, в свойствах проекта выбираем конфигурацию "All configurations", платформа - "All platforms", в разделе Configuration Properties выбираем подраздел General, задаем C++ Language Standard: ISO C++20 Standard (/std:c++20); в свойствах Advanced выбираем Character Set и указываем значение Use Unicode Character Set. В свойствах Linker-Sytem-SubSystem выбираем Windows (/SUBSYSTEM:WINDOWS).

2.2 Добавляем страницу свойств wxwidgets.props
Во вкладке "Solution Explorer" выбираем проект WxEmpty, далее выполняем: меню - View - Property Manager, появляется окно Property Manager. Выбираем проект WxEmpty, разворачиваем его. Удерживая клавишу Ctrl, выделяем две конфигурации Win32 (Debug и Release), щелкаем по выделению правой кнопкой мыши, выбираем пункт Add Existing Project Propery Sheet. выбираем wxwidgets.props в директории D:\Cpp\Lib, жмем кнопку Открыть.

2.3 Добавляем файлы main.cpp и pch.h. 
Добавляем упомянутые файлы, в pch.h вводим:

#pragma once
vc 
В main.cpp вводим:

#include "pch.h"

class wx_main : public wxApp
{
public:
virtual bool OnInit() { return true; };
};

wxIMPLEMENT_APP(wx_main);

Для файла main.cpp определяем Precompiled Header как "Create/Yc", pch.h:

... в принципе, precompiled header можно задать и для всего проекта. И отключать данную опцию для файлов, где сие не нужно (например, с библиотечными утилитами).

3. Компилируем.

Данное приложение можно экспортировать как шаблон: Project - Export Template... Перед экспортом, в состав проекта следует включить файл страницы свойств D:\Cpp\Lib\wx\wxwidgets.props, создаваемые на основе приложения не будут ссылаться не страницу свойств.

4. Добавляем формы wxFormBuilder.

Файл проекта wxFormBuilder и сгенерированные им файлы разместим в подкаталоге gui.
Для того, чтобы файлы из этого каталога "видели" файл заголовочный файл pch.h, в путь дополнительных каталогов файлов включения (Additional Include Directories) проекта добавим переменную $(ProjectDir):



Запускаем wxFormBuilder, обзываем проект gui_(с подчеркиванием!) , сохраняем файл под именем gui.fbp (без подчеркивания).
Далее задаем имена файлов cpp и h , в которых будут помещаться базовые классы фреймов wxWidgets. Имя задается в свойстве file проекта fbp. Также включаем опцию use_microsoft_bom и задаем значение свойства precompiled_header как #include "pch.h".





В проект добавляем фрейм Frame, именуем его frmMain. На фрейм накладываем сайзер wxBoxSizer, который именуем szrMain, title задаем как Заголовок. На сайзер szrMain накладываем два сайзера wxBoxSizer, которые именуем szrLeft и szrRight, у всех сайзеров устанавливаем значение свойства orient в wxVertical, у szrLeft значение свойства proportion в 1
На сайзере szrRight размещаем кнопку wxButton. Задаем свойства кнопки:
- wxButton.name: btn_addData;
- wxAnyButton.label: Добавление данных

На сайзере szrLeft размещаем грид wxGrid, который именуем как grMain.

Настройка грида grMain.

Задаем свойства:
- name: grMain;
- Grid.rows: 0;
- Grid.cols: 1;
- Grid.editing: false;
- Columns.columns_sizes: 80, 330;
- Columns.col_label_values: "Время" "Событие";
- Columns.col_label_horiz_alignment: wxALIGN_LEFT;
- Rows.row_label_size: 0;
- sizeritem.proportion: 1:
- sizeritembase.flag: wxALL, wxExpand

Должна получиться вот такой фрейм:





Генерация базовых классов.
В wxFormBuilder жмем кнопку Generate Code (F8). В подкаталоге gui появляются два файла: gui.cpp и gui.h. При изменении fbp - проекта и последующей перегенерации кода, эти файлы всякий раз создаются заново, поэтому редактировать их не стоит: перегенерация похерит все изменения. Настройка фреймов будет выполняться в классах - наследниках. Выполним генерацию классов-наследников, нажав клавишу F6

Следует установить "галку" возле имени фрейма и, если нужно, изменить имя самбкласса и/или имя файлов cpp/h. Для редактирования следует кликнуть мышкой справа от имени фрейма.
Видим, что имя формируется из имени fbp проекта "gui_" и имени базового класса "frmMain", то есть, символ подчеркивания в имени fbp проекта был нужен для отделения префикса "gui" от имени базового класса.

Проект cpp должен собираться без ошибок. 

Отображаем фрейм.

Модифицируем main.cpp:
#include "pch.h"
#include "gui/gui_frmMain.h"

class wx_main : public wxApp
{
public:
virtual bool OnInit() { 
auto * frmMain = new gui_frmMain((wxWindow *) nullptr);
frmMain->Show();
SetTopWindow(frmMain);
return true; 
};
};

wxIMPLEMENT_APP(wx_main);

Наполнение грида.

В гриде будут отображаться различные сообщения.

Грид wsGrid может работать с различными источниками  данных. Создадим собственный источник, реализовав наследник "почти абстрактного" класса (см. wxGridTableBase: https://docs.wxwidgets.org/3.1.5/classwx_grid_table_base.html).

Данные будут храниться в контейнере типа deque, элемент данных - пара "время, строка":
using data_row_type = std::pair<time_t, std::wstring>;


Добавляем в cpp-проект класс ds_logo. 
ds_logo.h:

#pragma once
#include "pch.h"
#include "wx/generic/grid.h"
#include <deque>

using data_row_type = std::pair<time_t, std::wstring>;

class ds_logo :
public  wxGridTableBase
{
public:
~ds_logo() { delete data; };

int GetNumberCols() override { return 2; }
int GetNumberRows() override { return data->size(); }

wxString GetValue(int row, int col) override;
void SetValue(int row, int col, const wxString& value) override {}; // Read-only
wxString GetColLabelValue(int col) override;


bool IsEmptyCell(int, int) override { return false; }
void Clear() override { data->clear(); }
void push_back(const std::wstring& text); // Дополнительный метод для заливки данных

private:
std::deque<data_row_type>* data = new std::deque<data_row_type>;

};

ds_logo.cpp:

#include "pch.h"
#include "ds_logo.h"

wxString ds_logo::GetValue(int row, int col)
{
wxString result;
auto& data_row = data->at(row);
switch (col)
{

case 0:
{
auto dt = data_row.first;

tm* ltm = std::localtime(&dt);
wchar_t mbstr[100];
std::wcsftime(mbstr, 100, L"%d-%m-%Y %T", ltm);
std::wstring dateAjoutSysteme(mbstr);
result = mbstr; // dateAjoutSysteme;
break;
}
case 1:
{
result = data_row.second;
break;
}
}

return result;

}

wxString ds_logo::GetColLabelValue(int col)
{
switch (col) {
case 0: return L"Время";
case 1: return L"Событие";
default: return L"";
}
}

void ds_logo::push_back(const std::wstring& text)
{
if (data->size() >= 11000)
data->erase(data->begin(), data->begin() + 1000);

data->push_back({ std::time(0) ,text });
auto grid = GetView();

wxGridTableMessage msg(this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1);
grid->ProcessTableMessage(msg);

grid->SelectRow(data->size() - 1);
grid->ScrollLines(data->size());
grid->ShowRow(data->size() - 1);

grid->ForceRefresh();
}

В производном классе реализованы: деструктор  ~ds_logo(),  а также перекрыты методы GetNumberCols()GetNumberRows(), GetValue(), SetValue()IsEmptyCell(), GetColLabelValue( ) и Clear(), а также добавлен метод  push_back(const std::wstring& text) - для заливки отображаемых данных.

Обработка события "Нажатие кнопки".

В дизайнере wxFormBuilder выбираем кнопку btn_addData, открываем закладку Events и для события wxButton.OnButtonClick задаем значение btn_addData_click. Жмем кнопку F8(генерация кода базовых классов).
В файле gui.h появились строки:
// Virtual event handlers, override them in your derived class
virtual void btn_addData_click( wxCommandEvent& event ) { event.Skip(); }
Т.е., в производном классе gui_frmMain следует переопределить виртуальный метод btn_addData_click.  Сделаем это.

gui_frmMain.h:
#ifndef __gui_frmMain__
#define __gui_frmMain__

/**
@file
Subclass of frmMain, which is generated by wxFormBuilder.
*/

#include "gui.h"

//// end generated include

/** Implementing frmMain */
class gui_frmMain : public frmMain
{
public:
/** Constructor */
gui_frmMain( wxWindow* parent );
//// end generated class members

protected:
void btn_addData_click(wxCommandEvent& event) override;

};

#endif // __gui_frmMain__

gui_frmMain.cpp:





Комментариев нет:

Отправить комментарий