Программирование на .NET в Linux

Часть 4. Элементы Gtk# в примерах

Библиотека Gtk содержит множество тонкостей, которые отличают ее от аналогичных библиотек UI. Взять, к примеру, способ расположения виджетов в окне. Те, у кого есть опыт программирования в Windows, наверняка не сомневаются, что где виджет "поставишь", там он и будет держаться. Отсюда следует проблема: при изменении размеров окна виджеты тоже нужно пропорционально изменить и передвинуть. Библиотека Gtk решает эту проблему кардинально. Виджеты специальным образом упаковываются в невидимые контейнеры, после чего их размеры и расположение будут меняться пропорционально размерам окна автоматически. Рассмотрим пример.

public MyWindow () : base ("MyWindow")
{
HBox hBox = new HBox (false, 4);
HBox hBox2 = new HBox (false, 4);
VBox vBox = new VBox (false, 4);

hBox.PackStart (new Button ("Button1"), true, true, 0);
hBox.PackStart (new Button ("Button2"), true, true, 0);
hBox.PackStart (new Button ("Button3"), true, true, 0);

Button b = new Button ("Button4");
b.SetUsize(70,150);
hBox2.PackStart (b, true, false, 0);
b = new Button ("Button5");
b.SetUsize(70,150);
hBox2.PackStart (b, true, false, 0);

vBox.Add (hBox);
vBox.Add (hBox2);
this.Add (vBox);

this.SetDefaultSize (200, 200);
this.DeleteEvent += new DeleteEventHandler (OnMyWindowDelete);
this.ShowAll ();
}

На скриншоте видно, как загадочным образом расположились 5 кнопок, хотя нигде не указаны их координаты, а для первых трех — еще и размеры. Зато в коде используются таинственные элементы VBox и HBox. Впрочем, вы наверняка уже догадались, что это и есть те самые контейнеры, которые самостоятельно умеют расположить виджеты в окне. Оба контейнера наследуют от базового класса Gtk.Box и представляют собой вертикальную и горизонтальную его версии. Конструктор HBox выглядит следующим образом:

public HBox (bool homogeneous, int spacing)

Параметр homogeneous, равный true, заставляет контейнер устанавливать одинаковые размеры для всех своих виджетов. Параметр spacing задает количество пикселей между виджетами контейнера. Конструктор VBox выглядит аналогично. В нашем примере один вертикальный контейнер содержит два горизонтальных, расположенных один под другим. Для упаковки виджетов в контейнер можно использовать один из нескольких методов, основной из которых — PackStart().

public void PackStart (Widget child, bool expand, bool fill, uint padding)

Здесь child — дочерний виджет; expand, установленный в true, указывает дочернему виджету занимать доступное пространство (т.е. пространство делится между виджетами с параметром expand, равным true); fill указывает заполнять все возможное пространство контейнера, полученное при условии expand == true (иначе вокруг виджета будет просто пустое пространство, а его размер останется неизменным); padding указывает незанятое пространство вокруг виджета в пикселях. Метод класса Gtk.Widget.SetUsize() задает минимальные размеры виджета (символ "U" в названии метода означает "User"). Правда, этот метод содержится только в Gtk 2.4 и старше, в следующие версии планируется его не включать. Вот еще пример использования контейнеров виджетов.

Entry e2;
Entry e3;
public MyWindow () : base ("MyWindow")
{
HBox hb1 = new HBox(false,4);
HBox hb2 = new HBox(false,4);
VBox vb1 = new VBox(false,4);
VBox vb2 = new VBox(false,4);
VBox vb3 = new VBox(false,4);

Label l1 = new Label("<span size='large' weight='bold' style='italic'>String transfer example</span>");
l1.UseMarkup = true;
hb1.Add(l1);

Label l2 = new Label("_Enter text:");
l2.Xalign = 1;
vb2.PackStart(l2,true,false,4);

e2 = new Entry();
e2.Changed += new EventHandler(Transfer);
l2.MnemonicWidget = e2;
vb3.PackStart(e2,true,true,4);

Label l3 = new Label("_Transferred:");
l3.Xalign = 1;
vb2.PackStart(l3,true,false,4);

e3 = new Entry();
l3.MnemonicWidget = e3;
vb3.PackStart(e3,true,true,4);

hb2.Add(vb2);
hb2.Add(vb3);

vb1.Add(hb1);
vb1.Add(hb2);
this.Add(vb1);
this.SetDefaultSize (220, 100);
this.DeleteEvent += new DeleteEventHandler (OnMyWindowDelete);
this.ShowAll ();
}
void Transfer(object sender, EventArgs e)
{
e3.Text = e2.Text;
}

В этом примере использованы все технологии, рассмотренные в предыдущих частях. Заголовочная метка l1 создается при помощи Pango, который позволяет задавать атрибуты строки при помощи размеченного в формате XML текста. По-новому скомбинированные контейнеры позволяют в тонкостях настроить расположение и размеры виджетов. Показана еще одна интересная особенность виджетов Gtk. Если при создании метки Label в тексте поставить знак подчеркивания перед какой-нибудь буквой, то она автоматически станет акселератором и будет доступна по комбинации Alt-буква. А элемент интерфейса, связанный с этим акселератором, задается свойством Label.MnemonicWidget. Рассмотрим последний пример, иллюстрирующий работу с самым сложным элементом Gtk — виджетом "дерево".

public MyWindow () : base ("MyWindow")
{
TreeView tv = new TreeView();
tv.HeadersVisible = true;

TreeViewColumn tc = new TreeViewColumn();
CellRenderer cr = new CellRendererText();
tc.Title = "Column 1";
tc.PackStart(cr,true);
tc.AddAttribute(cr,"text", 0);

tv.AppendColumn(tc);

tc = new TreeViewColumn();
cr = new CellRendererText();
tc.Title = "Column 2";
tc.PackStart(cr,true);
tc.AddAttribute(cr,"text", 1);

tv.AppendColumn(tc);

TreeStore ts = new TreeStore(typeof(string),typeof(string));
tv.Model = ts;

for (int i = 0; i < 4; i++)
{
TreeIter iter = ts.AppendValues("Point"+i.ToString(),"no text");
for (int j = 0; j < 3; j++)
{
ts.AppendValues(iter,"empty","Child"+j.ToString());
}
}

this.Add(tv);
this.SetDefaultSize (200, 300);
this.DeleteEvent += new DeleteEventHandler (OnMyWindowDelete);
this.ShowAll ();
}

Виджет TreeView — один из самых полезных и наиболее часто используемых в Gtk. Его дизайн реализует всем известный паттерн проектирования MVC (Model-View-Controller). Хотя, по моему личному мнению, архитектура виджета "дерево" ближе к модели "документ-представление". Дело в том, что функциональность объекта-контроллера в данном случае не выделена в отдельный класс, а распределена между классами TreeStore (модель, или документ) и TreeVew (вид, или представление). Создавать объект-контроллер имеет смысл только при наличии нескольких объектов представления для одного документа. Объект представления дерева состоит из колонок. Каждая колонка может отрисовываться по-разному: это может быть текст, картинка, поле для галочки и т.д. — все зависит от конкретной реализации подклассов CellRenderer'а.

В нашем примере показано, как в TreeView добавляются две колонки с текстом. При создании класса TreeStore также нужно указать, какого типа будут колонки. Их количество определяется автоматически, т.к. конструктор (TreeStore(params System.Type[]) принимает в качестве своего параметра массив объектов. Аналогично действует метод AppendValues (AppendValues(params object[]) : TreeIter). Он возвращает итератор, который затем можно использовать для добавления дочерних элементов.

Дмитрий Бушенко, nop@list.ru


Компьютерная газета. Статья была опубликована в номере 06 за 2006 год в рубрике программирование

©1997-2024 Компьютерная газета