Организуем доступ к базам данных SQLite при разработке кросс-платформенных приложений на C++/wxWidgets – Часть 2 – wxActiveRecordGenerator (wxARG)

Продолжение статьи, рассказывающей о работе с базами данных в wxWidgets. Первую чать статьи можно почитать здесь.
В предыдущей части мы рассмотрели настройку проекта, сборку дополнительных библиотек и создание графического интерфейса приложения. В этой часит рассказывается как создать классы бизнес-логики с помощью утилиты wxARG и о том, как эти классы использовать в своем проекте.

Устанавливаем утилиту wxARG, копируем из папки, куда была установлена wxARG два файла wxActiveRecord.h и wxActiveRecord.cpp в папку SQLiteTest/wxActiveRecord
Здесь хотелось бы отметить, что файлы wxActiveRecord.h и wxActiveRecord.cpp из последней верчии wxARG-1.2.0-rc2 отлично собрались вместе с тестовым проектом, хотя сама утилита работала нестабильно, из-за чего мне пришлось установить поверх нее более раннюю версию wxARG-1.1.0, которая работала нормально.
Итак, запускаем wxARG и выбираем пункт меню File -> Connect to database, в диалоговом окне Database Connection указываем тип базы данных SQLite3 Database и путь к файлу addressbook.db, созданному нашим тестовым приложением. Жмем Connect

В диалоговом окне Tables выбираем таблицы groups и persons. Жмем OK

После этого в левой части главного окна wxARG появится список с выбранными таблицами.
Каждая таблица нашей базы данных имеет ключевое поле id, поэтому на вкладке Properties для каждой таблицы в поле ID Field мы указываем поле id и включаем маркер Overwrite (delete custom stuff).

В нашей базе данных между таблицами groups и persons существует связь один-ко-многим, т.е. каждая группа может содержать множество контактов адресной книги. Переходим на вкладку Relations.
Для таблицы groups:
– Жмем кнопку Has Many
– В диалоговом окне Relation Properties указываем название таблицы со стороны “многие”, в нашем случае это таблица persons
– Указываем поле внешнего ключа в таблице persons. В нашем случае это groupid
Значения остальных параметров можно оставить без изменений.

Для таблицы persons:

  • Жмем кнопку Belongs To
  • В диалоговом окне Relation Properties указываем название таблицы со стороны “один”, в нашем случае это таблица groups
  • Указываем поле внешнего ключа в таблице persons. В нашем случае это groupid

Значения остальных параметров можно оставить без изменений.

В поле Output Dir указываем папку проекта SQLiteTest и жмем Generate
После всех проделанных операций, в папке с проектом SQLiteTest должны появиться файлы:

  • Group.h
  • Group.cpp
  • Person.h
  • Person.cpp

Эти файлы нам необходимо добавить в проект.

После того, как все необходимые файлы добавлены, можно приступать к внесению изменений в исходный код.
Для начала в файле wxActiveRecord.h не обходимо раскомментировать строку с макросом AR_USE_SQLITE, который указывает, что мы используем СУБД SQLite. В файле wxActiveRecord.cpp необходимо исправить путь к заголовочному файлу wxActiveRecord.h

wxActiveRecord.h

...
// COMMENT OUT THE ONES YOU DON'T USE

//#define AR_USE_POSTGRESQL
#define AR_USE_SQLITE
//#define AR_USE_MYSQL
//#define AR_USE_FIREBIRD
...

wxActiveRecord.cpp

...

#include "wxActiveRecord.h"

//begin WX_ACTIVE_RECORD
...

Теперь, если посмотреть на исходный код файлов, сгенерированных утилитой wxARG, то можно заметить, что каждая пара .h/.cpp файлов содержит описание и реализацию трех классов:

  • XXX – класс таблицы
  • XXXRow – класс типизированной записи таблицы
  • XXXRowSet – типизированный список записей

где XXX – название таблицы.
И после всего сказанного, вооружившись полученными знаниями, продолжим наш “happy coding”.

SQLiteTestApp.h

...
#include <sqliteDatabaseLayer.h>
#include "Group.h"
#include "Person.h"

class SQLiteTestApp : public wxApp
{
 Group * m_GroupTable;
 Person * m_PersonTable;
 ...
public:
 ...
 Group * GetGroupTable();
 Person * GetPersonTable();
};
...

SQLiteTestApp.cpp

...
int SQLiteTestApp::OnExit()
{
 wxDELETE(m_PersonTable);
 wxDELETE(m_GroupTable);
 ...
}

bool SQLiteTestApp::ConnectToDatabase()
{
 ...
 try
 {
  m_GroupTable = new Group(wxGetApp().GetDatabase(), wxT("groups"));
  m_PersonTable = new Person(wxGetApp().GetDatabase(), wxT("persons"));
 }
 catch(DatabaseLayerException & e)
 {
  wxActiveRecord::ProcessException(e);
 }
 return true;
}
...
Group * SQLiteTestApp::GetGroupTable()
{
 return m_GroupTable;
}

Person * SQLiteTestApp::GetPersonTable()
{
 return m_PersonTable;
}

Итак, мы добавили новые члены класса SQLiteTestApp, обеспечивающие доступ к таблицам базы данных и теперь можно приступать к созданию GUI.
Дистрибутив wxWidgets содержит несколько десятков свободно распространяемых иконок, которые мы можем использовать в нашем приложении. Создадим папку SQLiteTest/art и скопируем в нее файлы

  • $(WXWIN)/art/addbookm.xpm
  • $(WXWIN)/art/delbookm.xpm
  • $(WXWIN)/art/new.xpm
  • $(WXWIN)/art/delete.xpm

SQLiteTestMainFrame.h

#ifndef _SQLITE_TEST_MAINFRAME_H
#define _SQLITE_TEST_MAINFRAME_H

#include <wx/wx.h>
#include <wx/toolbar.h>
#include <wx/listbox.h>
#include <wx/listctrl.h>
#include <wx/html/htmlwin.h>

class SQLiteTestMainFrame : public wxFrame
{
 wxListBox * m_GroupsListBox;
 wxListView * m_PersonsListView;
 wxHtmlWindow * m_PersonInfoPanel;
 void CreateControls();
 wxToolBar * CreateToolBar();
public:
 SQLiteTestMainFrame();
 bool Create(wxWindow * parent, wxWindowID id, const wxString & title);

 DECLARE_EVENT_TABLE()
 void OnExit(wxCommandEvent & event);
};

#endif

SQLiteTestMainFrame.cpp

...
#include <wx/splitter.h>
#include "new.xpm"
#include "delete.xpm"
#include "addbookm.xpm"
#include "delbookm.xpm"

enum
{
 ID_GROUPS_LISTBOX = 10001,
 ID_PERSONS_LISTCTRL,
 ID_PERSON_INFO_PANEL,
 ID_ADD_GROUP,
 ID_DELETE_GROUP,
 ID_ADD_PERSON,
 ID_DELETE_PERSON
};
...
void SQLiteTestMainFrame::CreateControls()
{
 wxMenuBar * menuBar = new wxMenuBar;
 SetMenuBar(menuBar);

 wxMenu * fileMenu = new wxMenu;
 fileMenu->Append(wxID_EXIT, _("Exit\tAlt+F4"));

 menuBar->Append(fileMenu, _("File"));

 wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
 SetSizer(sizer);

 wxSplitterWindow * splitter = new wxSplitterWindow(this, wxID_ANY,
  wxDefaultPosition, wxSize(500, 400), wxSP_3DSASH);
 splitter->SetMinimumPaneSize(100);
 sizer->Add(splitter, 1, wxEXPAND);

 m_GroupsListBox = new wxListBox(splitter, ID_GROUPS_LISTBOX,
  wxDefaultPosition, wxDefaultSize);

 wxSplitterWindow * personsplitter = new wxSplitterWindow(splitter, wxID_ANY,
  wxDefaultPosition, wxSize(500, 400), wxSP_3DSASH);
 personsplitter->SetMinimumPaneSize(100);
 m_PersonsListView = new wxListView(personsplitter, ID_PERSONS_LISTCTRL,
  wxDefaultPosition, wxDefaultSize, wxLC_REPORT);
 m_PersonsListView->InsertColumn(0, _("First Name"), wxLIST_FORMAT_LEFT, 120);
 m_PersonsListView->InsertColumn(1, _("Last Name"), wxLIST_FORMAT_LEFT, 120);
 m_PersonsListView->InsertColumn(2, _("E-Mail"), wxLIST_FORMAT_LEFT, 130);
 m_PersonsListView->InsertColumn(3, _("Phone"), wxLIST_FORMAT_LEFT, 130);

 m_PersonInfoPanel = new wxHtmlWindow(personsplitter, ID_PERSON_INFO_PANEL);

 personsplitter->SetSashGravity(1.0);

 splitter->SplitVertically(m_GroupsListBox, personsplitter, 160);
 personsplitter->SplitHorizontally(m_PersonsListView, m_PersonInfoPanel,
  personsplitter->GetSize().GetHeight()-180);

 SetToolBar(CreateToolBar());

 CreateStatusBar(2);
 Centre();
}

wxToolBar * SQLiteTestMainFrame::CreateToolBar()
{
 wxToolBar * toolBar = new wxToolBar(this, wxID_ANY, wxDefaultPosition,
  wxDefaultSize, wxTB_FLAT|wxTB_TEXT);
 toolBar->AddTool(ID_ADD_GROUP, _("Add Group"), wxBitmap(addbookm_xpm));
 toolBar->AddTool(ID_DELETE_GROUP, _("Remove Group"), wxBitmap(delbookm_xpm));
 toolBar->AddSeparator();
 toolBar->AddTool(ID_ADD_PERSON, _("Add Person"), wxBitmap(new_xpm));
 toolBar->AddTool(ID_DELETE_PERSON, _("Remove Person"), wxBitmap(delete_xpm));
 toolBar->Realize();
 return toolBar;
}
...

Для работы с классом wxHtmlWindow в свойствах проекта вразделе Linker -> Input в настройках Additional Dependencies нам необходимо добавить библиотеку wxmsw28ud_html.lib для конфигурации Debug и wxmsw28u_html.lib для конфигурации Release
После всех внесенных изменений, у нас должно получиться нечто подобное:

Отлично, интерфейс приложения готов, теперь можно приступить к реализации функционала.

RecordIDClientData.h

#ifndef _RECORD_ID_CLIENT_DATA_H
#define _RECORD_ID_CLIENT_DATA_H

#include <wx/clntdata.h>

class RecordIDClientData : public wxClientData
{
 int m_ID;
public:
 RecordIDClientData(int id) : m_ID(id) {}

 int GetID() {return m_ID;}
 void SetID(int id) {m_ID = id;}
};

#endif

SQLiteTestMainFrame.h

...
#include <wx/wx.h>
#include <wx/toolbar.h>
#include <wx/listbox.h>
#include <wx/listctrl.h>
#include <wx/html/htmlwin.h>

class SQLiteTestMainFrame : public wxFrame
{
 ...
 void FillGroupsList();
 void FillPersonsList(int groupid);
 ...
 DECLARE_EVENT_TABLE()
 void OnExit(wxCommandEvent & event);
 void OnGroupListBoxSelected(wxCommandEvent & event);
 void OnPersonListViewSelected(wxListEvent & event);
 void OnPersonInfoPanelLinkClicked(wxHtmlLinkEvent & event);
};
...

SQLiteTestMainFrame.cpp

void SQLiteTestMainFrame::FillGroupsList()
{
 m_GroupsListBox->Freeze();
 m_GroupsListBox->Clear();
 GroupRowSet * allGroups = wxGetApp().GetGroupTable()->All();
 for(unsigned long i = 0; i < allGroups->Count(); ++i)
 {
  m_GroupsListBox->Append(allGroups->Item(i)->name,
   new RecordIDClientData(allGroups->Item(i)->id));
 }
 if(m_GroupsListBox->GetCount())
 {
  m_GroupsListBox->SetSelection(0);
  RecordIDClientData * data = (RecordIDClientData *)
   m_GroupsListBox->GetClientObject(m_GroupsListBox->GetSelection());
  if(data)
  {
   FillPersonsList(data->GetID());
  }
 }
 wxGetApp().GetGroupTable()->CollectRowSet(allGroups);
 m_GroupsListBox->Thaw();
}

void SQLiteTestMainFrame::FillPersonsList(int groupid)
{
 m_PersonsListView->Freeze();
 m_PersonsListView->DeleteAllItems();
 GroupRow * thisGroup = wxGetApp().GetGroupTable()->Id(groupid);
 if(thisGroup)
 {
  PersonRowSet * allPersons = thisGroup->GetPersons();
  long item(0);
  for(unsigned long i = 0; i < allPersons->Count(); ++i)
  {
   item = m_PersonsListView->InsertItem(item, allPersons->Item(i)->first_name);
   m_PersonsListView->SetItem(item, 1, allPersons->Item(i)->last_name);
   m_PersonsListView->SetItem(item, 2, allPersons->Item(i)->email);
   m_PersonsListView->SetItem(item, 3, allPersons->Item(i)->phone);
   m_PersonsListView->SetItemData(item, (long)allPersons->Item(i)->id);
  }
  thisGroup->CollectRowSet(allPersons);
  if(m_PersonsListView->GetItemCount())
  {
   m_PersonsListView->Select(0);
  }
  else
  {
   m_PersonInfoPanel->SetPage(wxT("<html><body></body></html>"));
  }
 }
 wxGetApp().GetGroupTable()->CollectRow(thisGroup);
 m_PersonsListView->Thaw();
}

void SQLiteTestMainFrame::
        OnGroupListBoxSelected(wxCommandEvent & event)
{
 RecordIDClientData * data =
     (RecordIDClientData *)event.GetClientObject();
 if(data)
 {
  FillPersonsList(data->GetID());
 }
}

void SQLiteTestMainFrame::OnPersonListViewSelected(wxListEvent & event)
{
 long personid = event.GetData();
 PersonRow * person = wxGetApp().GetPersonTable()->Id((int)personid);
 if(person)
 {
  m_PersonInfoPanel->SetPage(wxString::Format(
   wxT("<html><body><h3>%s %s<h3><body><html>"),
   person->first_name, person->last_name));
  m_PersonInfoPanel->AppendToPage(wxString::Format(
   wxT("<b>Gender: </b> %s"),
   (person->gender?wxT("Male"):wxT("Female"))));
  m_PersonInfoPanel->AppendToPage(wxT("<hr />"));
  m_PersonInfoPanel->AppendToPage(wxString::Format(
   wxT("<b>Address: </b> %s<br />"),
   person->address.GetData()));
  m_PersonInfoPanel->AppendToPage(wxString::Format(
   wxT("<b>City: </b> %s<br />"),
   person->city.GetData()));
  m_PersonInfoPanel->AppendToPage(wxString::Format(
   wxT("<b>Country: </b> %s"),
   person->country.GetData()));
  m_PersonInfoPanel->AppendToPage(wxT("<hr />"));
  m_PersonInfoPanel->AppendToPage(
   wxString::Format(wxT("<b>Phone: </b> %s<br />"),
   person->phone.GetData()));
  m_PersonInfoPanel->AppendToPage(wxString::Format(
   wxT("<b>E-mail: </b> <a href=\"mailto:%s\">%s</a><br />"),
   person->email, person->email.GetData()));
 }
 else
 {
  m_PersonInfoPanel->SetPage(
   _("<html><body><h3>Can't find info about selected person<h3><body><html>"));
 }
}

void SQLiteTestMainFrame::
    OnPersonInfoPanelLinkClicked(wxHtmlLinkEvent & event)
{
#if defined(__WXMSW__)
 ShellExecute(NULL, NULL,
     event.GetLinkInfo().GetHref().GetData(),
     NULL, NULL, SW_SHOW);
#else
 wxExecute(event.GetLinkInfo().GetHref());
#endif
}

Список в левой части окна содержит названия групп. Каждый элемент списка содержит код группы. Привязка данных к элементам списка реализована с помощью объектов класса RecordIDClientData, каждый объект которого содержит код группы. Класс RecordIDClientData является производным от wxClientData и его использование обеспечивает автоматическую очистку памяти при удалении элемента списка. Доступ к данным, ассоциированным с элементом списка групп, производится посредством метода GetClientObject класса wxListBox
При выборе записи в списке групп, список контактов в правой части окна заполняется данными контактов данной группы.

При выборе элемента списка контактов, информационное поле в нижней части окна заполняется данными контакта, связанного с выбранным элементом списка. Каждый элемент списка контактов содержит в поле данных код контакта. Доступ к полю данных производится посредством метода GetItemData класса wxListView

При нажатии на ссылку с адресом электронной почты в информационном поле, создается новое письмо в почтовом клиенте, назначенном по умолчанию.

Итак, с отображением данных мы закончили. Теперь нужно реализовать добавление новой записи в таблицу и удаление записей из таблицы.
Для начала создадим диалоговые окна вода данных.

EditGroupDialog.h

#ifndef _EDIT_GROUP_DIALOG_H
#define _EDIT_GROUP_DIALOG_H

#include <wx/wx.h>

class EditGroupDialog : public wxDialog
{
 wxString m_GroupName;
 wxString m_GroupDescription;
 void CreateControls();
public:
 EditGroupDialog(wxWindow * parent);
 bool Create(wxWindow * parent, wxWindowID id, const wxString title);

 const wxString & GetGroupName();
 void SetGroupName(const wxString & value);
 const wxString & GetGroupDescription();
 void SetGroupDescription(const wxString & value);
};

#endif

EditGroupDialog.cpp

#include "EditGroupDialog.h"
#include <wx/valgen.h>

enum
{
 ID_EGD_NAME_TEXTCTRL = 10001,
 ID_EGD_DESCRIPTION_TEXTCTRL
};

EditGroupDialog::EditGroupDialog(wxWindow * parent)
{
 Create(parent, wxID_ANY, _("Editing group"));
}

bool EditGroupDialog::Create(wxWindow * parent, wxWindowID id, const wxString title)
{
 bool res = wxDialog::Create(parent, id, title);
 if(res)
 {
  CreateControls();
 }
 return res;
}

void EditGroupDialog::CreateControls()
{
 wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
 SetSizer(sizer);

 wxStaticText * nameLabel = new wxStaticText(this, wxID_ANY, _("Name:"));
 wxStaticText * descriptionLabel = new wxStaticText(this, wxID_ANY, _("Description:"));

 wxTextCtrl * nameEdit = new wxTextCtrl(this, ID_EGD_NAME_TEXTCTRL, wxEmptyString);
 wxTextCtrl * descriptionEdit = new wxTextCtrl(this, ID_EGD_DESCRIPTION_TEXTCTRL,
  wxEmptyString, wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE);
 nameEdit->SetValidator(wxGenericValidator(&m_GroupName));
 descriptionEdit->SetValidator(wxGenericValidator(&m_GroupDescription));

 wxFlexGridSizer * fg_sizer = new wxFlexGridSizer(2, 2, 0, 0);
 fg_sizer->Add(nameLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(nameEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(descriptionLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(descriptionEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->AddGrowableCol(1);

 sizer->Add(fg_sizer, 1, wxEXPAND|wxTOP|wxLEFT|wxRIGHT, 5);
 sizer->Add(CreateButtonSizer(wxID_OK|wxID_CANCEL), 0, wxALIGN_RIGHT|wxALL, 5);
}

const wxString & EditGroupDialog::GetGroupName()
{
 return m_GroupName;
}

void EditGroupDialog::SetGroupName(const wxString & value)
{
 m_GroupName = value;
}

const wxString & EditGroupDialog::GetGroupDescription()
{
 return m_GroupDescription;
}

void EditGroupDialog::SetGroupDescription(const wxString & value)
{
 m_GroupDescription = value;
}

EditPersonDialog.h

#ifndef _EDIT_PERSON_DIALOG_H
#define _EDIT_PERSON_DIALOG_H

#include <wx/wx.h>

class EditPersonDialog : public wxDialog
{
 wxString m_FirstName;
 wxString m_LastName;
 wxString m_Address;
 wxString m_City;
 wxString m_Country;
 wxString m_Email;
 wxString m_Phone;
 void CreateControls();
public:
 EditPersonDialog(wxWindow * parent);
 bool Create(wxWindow * parent, wxWindowID id, const wxString title);

 const wxString & GetFirstName();
 const wxString & GetLastName();
 const wxString & GetAddress();
 const wxString & GetCity();
 const wxString & GetCountry();
 const wxString & GetEmail();
 const wxString & GetPhone();
};

#endif

EditPersonDialog.cpp

#include "EditPersonDialog.h"
#include <wx/valgen.h>

enum
{
 ID_EPD_FIRSTNAME_TEXTCTRL = 10001,
 ID_EPD_LASTNAME_TEXTCTRL,
 ID_EPD_ADDRESS_TEXTCTRL,
 ID_EPD_CITY_TEXTCTRL,
 ID_EPD_COUNTRY_TEXTCTRL,
 ID_EPD_EMAIL_TEXTCTRL,
 ID_EPD_PHONE_TEXTCTRL
};

EditPersonDialog::EditPersonDialog(wxWindow * parent)
{
 Create(parent, wxID_ANY, _("Editing person"));
}

bool EditPersonDialog::Create(wxWindow * parent,
wxWindowID id, const wxString title)
{
 bool res = wxDialog::Create(parent, id, title);
 if(res)
 {
  CreateControls();
 }
 return res;
}

void EditPersonDialog::CreateControls()
{
 wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
 SetSizer(sizer);

 wxStaticText * firstnameLabel = new wxStaticText(this, wxID_ANY, _("First Name:"));
 wxStaticText * lastnameLabel = new wxStaticText(this, wxID_ANY, _("Last Name:"));
 wxStaticText * addressLabel = new wxStaticText(this, wxID_ANY, _("Address:"));
 wxStaticText * cityLabel = new wxStaticText(this, wxID_ANY, _("City:"));
 wxStaticText * countryLabel = new wxStaticText(this, wxID_ANY, _("Country:"));
 wxStaticText * emailLabel = new wxStaticText(this, wxID_ANY, _("E-mail:"));
 wxStaticText * phoneLabel = new wxStaticText(this, wxID_ANY, _("Phone:"));

 wxTextCtrl * firstnameEdit = new wxTextCtrl(this, ID_EPD_FIRSTNAME_TEXTCTRL, wxEmptyString);
 wxTextCtrl * lastnameEdit = new wxTextCtrl(this, ID_EPD_LASTNAME_TEXTCTRL, wxEmptyString);
 wxTextCtrl * addressEdit = new wxTextCtrl(this, ID_EPD_ADDRESS_TEXTCTRL, wxEmptyString);
 wxTextCtrl * cityEdit = new wxTextCtrl(this, ID_EPD_CITY_TEXTCTRL, wxEmptyString);
 wxTextCtrl * countryEdit = new wxTextCtrl(this, ID_EPD_COUNTRY_TEXTCTRL, wxEmptyString);
 wxTextCtrl * emailEdit = new wxTextCtrl(this, ID_EPD_EMAIL_TEXTCTRL, wxEmptyString);
 wxTextCtrl * phoneEdit = new wxTextCtrl(this, ID_EPD_PHONE_TEXTCTRL, wxEmptyString);

 firstnameEdit->SetMinSize(wxSize(150,-1));

 firstnameEdit->SetValidator(wxGenericValidator(&m_FirstName));
 lastnameEdit->SetValidator(wxGenericValidator(&m_LastName));
 addressEdit->SetValidator(wxGenericValidator(&m_Address));
 cityEdit->SetValidator(wxGenericValidator(&m_City));
 countryEdit->SetValidator(wxGenericValidator(&m_Country));
 emailEdit->SetValidator(wxGenericValidator(&m_Email));
 phoneEdit->SetValidator(wxGenericValidator(&m_Phone));

 wxFlexGridSizer * fg_sizer = new wxFlexGridSizer(2, 2, 0, 0);
 fg_sizer->Add(firstnameLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(firstnameEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(lastnameLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(lastnameEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(addressLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(addressEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(cityLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(cityEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(countryLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(countryEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(emailLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(emailEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(phoneLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->Add(phoneEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 fg_sizer->AddGrowableCol(1);

 sizer->Add(fg_sizer, 1, wxEXPAND|wxTOP|wxLEFT|wxRIGHT, 5);
 sizer->Add(CreateButtonSizer(wxID_OK|wxID_CANCEL), 0, wxALIGN_RIGHT|wxALL, 5);

 sizer->Fit(this);
}

const wxString & EditPersonDialog::GetFirstName()
{
 return m_FirstName;
}

const wxString & EditPersonDialog::GetLastName()
{
 return m_LastName;
}

const wxString & EditPersonDialog::GetAddress()
{
 return m_Address;
}

const wxString & EditPersonDialog::GetCity()
{
 return m_City;
}

const wxString & EditPersonDialog::GetCountry()
{
 return m_Country;
}

const wxString & EditPersonDialog::GetEmail()
{
 return m_Email;
}

const wxString & EditPersonDialog::GetPhone()
{
 return m_Phone;
}


Теперь можно приступать к написанию обработчиков событий от кнопок на панели инструментов.

SQLiteTestMainFrame.h

...
class SQLiteTestMainFrame : public wxFrame
{
 ...
 void OnAddGroup(wxCommandEvent & event);
 void OnRemoveGroup(wxCommandEvent & event);
 void OnAddPerson(wxCommandEvent & event);
 void OnRemovePerson(wxCommandEvent & event);
 void OnRemoveGroupUpdateUI(wxUpdateUIEvent & event);
 void OnAddPersonUpdateUI(wxUpdateUIEvent & event);
 void OnRemovePersonUpdateUI(wxUpdateUIEvent & event);
};
...

SQLiteTestMainFrame.cpp

...
void SQLiteTestMainFrame::OnAddGroup(wxCommandEvent & event)
{
 EditGroupDialog * dlg = new EditGroupDialog(this);
 if(dlg->ShowModal() == wxID_OK)
 {
  GroupRow * newGroup = wxGetApp().GetGroupTable()->New();
  newGroup->name = dlg->GetGroupName();
  newGroup->description = dlg->GetGroupDescription();
  newGroup->Save();
  wxGetApp().GetGroupTable()->CollectRow(newGroup);
  FillGroupsList();
 }
 dlg->Destroy();
}

void SQLiteTestMainFrame::OnRemoveGroup(wxCommandEvent & event)
{
 int selection = m_GroupsListBox->GetSelection();
 RecordIDClientData * data = (RecordIDClientData *)
  m_GroupsListBox->GetClientObject(selection);
 if(data)
 {
  GroupRow * thisGroup = wxGetApp().GetGroupTable()->Id(data->GetID());
  if(thisGroup && (wxMessageBox(_("Do you really want to delete this group?"),
   _("Delete group"), wxYES_NO) == wxYES))
  {
   PersonRowSet * thisPersons = thisGroup->GetPersons();
   for(unsigned long i = 0; i < thisPersons->Count(); ++i)
   {
    thisPersons->Item(i)->Delete();
   }
   thisGroup->CollectRowSet(thisPersons);
   thisGroup->Delete();
   wxGetApp().GetGroupTable()->CollectRow(thisGroup);
   m_GroupsListBox->Delete(selection);
   if(m_GroupsListBox->GetCount())
   {
    m_GroupsListBox->SetSelection(selection <
     (int)m_GroupsListBox->GetCount() ? selection : 0);
    data = (RecordIDClientData *)m_GroupsListBox->GetClientObject(
     m_GroupsListBox->GetSelection());
    if(data)
    {
     FillPersonsList(data->GetID());
    }
   }
  }
 }
}

void SQLiteTestMainFrame::OnAddPerson(wxCommandEvent & event)
{
 RecordIDClientData * data = (RecordIDClientData *)
  m_GroupsListBox->GetClientObject(m_GroupsListBox->GetSelection());
 if(data)
 {
  GroupRow * thisGroup = wxGetApp().GetGroupTable()->Id(data->GetID());
  if(thisGroup)
  {
   EditPersonDialog * dlg = new EditPersonDialog(this);
   if(dlg->ShowModal() == wxID_OK)
   {
    PersonRow * newPerson = wxGetApp().GetPersonTable()->New();
    newPerson->groupid= thisGroup->id;
    newPerson->first_name = dlg->GetFirstName();
    newPerson->last_name = dlg->GetLastName();
    newPerson->address = dlg->GetAddress();
    newPerson->city = dlg->GetCity();
    newPerson->country = dlg->GetCountry();
    newPerson->email = dlg->GetEmail();
    newPerson->phone = dlg->GetPhone();
    newPerson->Save();
    wxGetApp().GetPersonTable()->CollectRow(newPerson);
    FillPersonsList(thisGroup->id);
   }
   dlg->Destroy();
  }
  wxGetApp().GetGroupTable()->CollectRow(thisGroup);
 }
}

void SQLiteTestMainFrame::OnRemovePerson(wxCommandEvent & event)
{
 long selection = m_PersonsListView->GetFirstSelected();
 PersonRow * thisPerson = wxGetApp().GetPersonTable()->Id(
  (int)m_PersonsListView->GetItemData(selection));
 if(thisPerson && (wxMessageBox(_("Do you really want to delete this record?"),
  _("Delete person"), wxYES_NO) == wxYES))
 {
  int groupid = thisPerson->groupid;
  thisPerson->Delete();
  wxGetApp().GetPersonTable()->CollectRow(thisPerson);
  m_PersonsListView->DeleteItem(selection);
  if(m_PersonsListView->GetItemCount())
  {
   m_PersonsListView->Select(wxMin(selection, m_PersonsListView->GetItemCount()-1));
  }
 }
}

void SQLiteTestMainFrame::
        OnRemoveGroupUpdateUI(wxUpdateUIEvent & event)
{
 event.Enable(m_GroupsListBox->GetSelection() >= 0);
}

void SQLiteTestMainFrame::
        OnAddPersonUpdateUI(wxUpdateUIEvent & event)
{
 event.Enable(m_GroupsListBox->GetSelection() >= 0);
}

void SQLiteTestMainFrame::
        OnRemovePersonUpdateUI(wxUpdateUIEvent & event)
{
 event.Enable(m_PersonsListView->GetFirstSelected() >= 0);
}
...

Как видно, кнопка удаления группы и кнопка добавления контакта активна только в случае, если есть активная запись в списке групп. Кнопка удаления контакта становится активной только в случае, если есть активная запись в списке контактов.
При удалении группы удаляются и все связанные с ней контакты.

Ну вот. Мы закончили. Теперь собираем Release-версию программы и тестируем. Можно переходить к созданию проекта под Linux
Продолжение статьи можно почитать здесь.

T-Rex

View Comments

  • На счет wxArg что то я нигде не могу его достать все ссылки битые каталоги пустые

    • На wxCode переходишь в раздел загрузок на sourceforge и там есть инсталлер.
      НО. Для простых баз он нормально работает, а вот для более сложных, да еще и с blob'ами надо будет руками подправлять.
      Ну и там еще в паре мест есть трабла, isNew переменная-член класса записи в конструкторе переобъявляется как локальная, надо руками убирать кусок кода, а то wxARG не будет знать что новая запись добавлена.
      Проект, как я понимаю, больше не сапортится. Я начал писать собственный кодогенератор, но там все только в зачаточном состоянии и я не знаю когда у меня будет время все закончить.

Share
Published by
T-Rex

Recent Posts

Разработка кроссплатформенных модульных приложений на C++ с библиотекой wxWidgets

Введение Уже долгое время не пишу статьи о разработке, хотя сам процесс написания мне очень…

10 years ago

wxWidgets App With Plugins (Windows/Linux/Mac) – Sample Source Code

I can see that there is still a lot of topics at wxWidgets forums related…

11 years ago

wxToolBox is Now Open-Source!

I've just published the source code of wxToolBox component and a couple of sample apps at…

11 years ago

Microsoft Kinect Helper Library and Sample for wxWidgets

Microsoft released their Kinect SDK several days ago. So, for those wxWidgets developers who are…

13 years ago

wxJSON 1.1.0 Released

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to…

14 years ago

wxRuby. Оно даже работает!

Вдохновленнный читаемой нынче книгой My Job Went to India: 52 Ways to Save Your Job…

15 years ago