Продолжение статьи, рассказывающей о работе с базами данных в 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
Продолжение статьи можно почитать здесь.





4 Comments
aminiy
На счет wxArg что то я нигде не могу его достать все ссылки битые каталоги пустые
T-Rex
На wxCode переходишь в раздел загрузок на sourceforge и там есть инсталлер.
НО. Для простых баз он нормально работает, а вот для более сложных, да еще и с blob’ами надо будет руками подправлять.
Ну и там еще в паре мест есть трабла, isNew переменная-член класса записи в конструкторе переобъявляется как локальная, надо руками убирать кусок кода, а то wxARG не будет знать что новая запись добавлена.
Проект, как я понимаю, больше не сапортится. Я начал писать собственный кодогенератор, но там все только в зачаточном состоянии и я не знаю когда у меня будет время все закончить.
jbb
http://wxcode.sourceforge.net/components/activerecord/tutorial/wxARG-tutorial.php
I cannot download wxARG http://wxcode.sourceforge.net/components/activerecord/tutorial/wxARG-tutorial.zip
admin
I think you should ask author of wxARG regarding this point.