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

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

Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1

Устанавливаем утилиту 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

Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1

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

Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1

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

Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1

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

Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1

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

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

Значения остальных параметров можно оставить без изменений.
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
В поле Output Dir указываем папку проекта SQLiteTest и жмем Generate
После всех проделанных операций, в папке с проектом SQLiteTest должны появиться файлы:

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

Эти файлы нам необходимо добавить в проект.
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1

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

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 
#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
После всех внесенных изменений, у нас должно получиться нечто подобное:
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
Отлично, интерфейс приложения готов, теперь можно приступить к реализации функционала.

RecordIDClientData.h

#ifndef _RECORD_ID_CLIENT_DATA_H
#define _RECORD_ID_CLIENT_DATA_H

#include 

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 
#include 
#include 
#include 
#include 

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(""));
		}
	}
	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("

%s %s

"), person->first_name, person->last_name)); m_PersonInfoPanel->AppendToPage(wxString::Format( wxT("Gender: %s"), (person->gender?wxT("Male"):wxT("Female")))); m_PersonInfoPanel->AppendToPage(wxT("
")); m_PersonInfoPanel->AppendToPage(wxString::Format( wxT("Address: %s
"), person->address.GetData())); m_PersonInfoPanel->AppendToPage(wxString::Format( wxT("City: %s
"), person->city.GetData())); m_PersonInfoPanel->AppendToPage(wxString::Format( wxT("Country: %s"), person->country.GetData())); m_PersonInfoPanel->AppendToPage(wxT("
")); m_PersonInfoPanel->AppendToPage( wxString::Format(wxT("Phone: %s
"), person->phone.GetData())); m_PersonInfoPanel->AppendToPage(wxString::Format( wxT("E-mail: %s
"), person->email, person->email.GetData())); } else { m_PersonInfoPanel->SetPage( _("

Can't find info about selected person

")); } } 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

При нажатии на ссылку с адресом электронной почты в информационном поле, создается новое письмо в почтовом клиенте, назначенном по умолчанию.
Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
Итак, с отображением данных мы закончили. Теперь нужно реализовать добавление новой записи в таблицу и удаление записей из таблицы.
Для начала создадим диалоговые окна вода данных.

EditGroupDialog.h

#ifndef _EDIT_GROUP_DIALOG_H
#define _EDIT_GROUP_DIALOG_H

#include 

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 

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;
}

Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1

EditPersonDialog.h

#ifndef _EDIT_PERSON_DIALOG_H
#define _EDIT_PERSON_DIALOG_H

#include 

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 

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;
}

Работа с базами данных в wxWidgets - SQLite + DatabaseLayer - 1
Теперь можно приступать к написанию обработчиков событий от кнопок на панели инструментов.

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

  1. aminiy   •  

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

  2. T-Rex   •     Author

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

  3. admin   •  

    I think you should ask author of wxARG regarding this point.

Leave a Reply

Your email address will not be published. Required fields are marked *

Please leave these two fields as-is: