Содержание


Управление геометрией виджетов в PyGTK

Обсуждаем вопросы размещения и выбора размеров виджетов-контейнеров

Comments

GTK+ и связанный с ним пакет PyGTK содержат несколько удобных контейнеров. Если необходимо разместить рядом несколько виджетов, часто достаточно использовать горизонтальный контейнер gtk.HBox или же вертикальный контейнер gtk.VBox. Если необходимо упорядочить элементы в двух направлениях, то можно использовать gtk.Table. Однако в некоторых случаях может понадобиться обеспечить более сложное размещение, используя для этого более точные требования.

В целом размещение и изменение размера виджетов не сильно загружает процессор, однако при этом необходимо учитывать различные соображения и детали. Создание подобного менеджера на основе интерпретируемого языка дает то преимущество, что позволяет быстро опробовать различные методы и настройки. Контейнер, который описывается в данной статье, может послужить разработчикам на PyGTK основой для создания или расширения собственных контейнеров. Также он может вдохновить на создание аналогичных контейнеров тех разработчиков, которые используют другие инструменты построения GUI.

Размещение в таблице

Давайте рассмотрим такой контейнер-таблицу, в котором размер пикселов для рядов и столбцов может изменяться в соответствии с заданными коэффициентами (весами). Однако прежде чем углубиться в детали, рассмотрим несколько примеров.

В каждом из приведенных ниже трех примеров используется свой набор из трех дочерних кнопок с различной политикой их размещения. Каждый пример сначала рассматривается для малого размера пиксела, затем размер пикселов увеличивается.

  1. Здесь мы рассматриваем единственную кнопку, для которой следует оставить расстояние, в точности равное 20 пикселов, до левого края, а также поровну распределить остающееся справа горизонтальное расстояние между самой кнопкой и пространством, которое остается до правого края. В вертикальном направлении мы распределяем свободное пространство в пропорции (1, 4, 2) для элементов (верхнее пространство, кнопка, нижнее пространство).
    Рисунок 1. Одна кнопка на маленькой форме
    Single button within a small space
    Рисунок 2. Одна кнопка на форме большего размера
    Single button within a bigger space
    Single button within a bigger space
  2. Теперь рассмотрим две кнопки, которые должны увеличиваться в пропорции 2:3 и 1:3 при появлении дополнительного горизонтального пространства.
    Рисунок 3. Две кнопки на форме малого размера
    Two buttons within a small space
    Рисунок 4. Две кнопки на большем пространстве
    Two buttons within a bigger space
    Two buttons within a bigger space
  3. Далее мы рассмотрим требования к расположению в нескольких столбцах, когда центральная кнопка "middle7/8" занимает 7/8 от возможных дополнительных горизонтальных пикселов.
    Рисунок 5. Требования к расположению в столбцах на небольшом пространстве
    Multi-columns requirements within a small space
    Рисунок 6. Требования к расположению в столбцах на увеличенном пространстве
    Multi-columns requirements within a bigger space
    Multi-columns requirements within a bigger space

Интерфейс

Виджет WTable мало отличается от стандартного gtk.Table. Он является производным от gtk.Container. Основные отличия:

  • Нет необходимости предварительно задавать количество строк и столбцов.
  • Метод attach виджета gtk.Table имеет дополнительные параметры xoptions=GTK.EXPAND|GTK.FILL, yoptions=GTK.EXPAND|GTK.FILL, xpadding=0, ypadding=0, тогда как наш метод WTable.attach использует дополнительный параметр glue, который будет описан ниже.
Листинг 1. Конструктор WTable
class WTable(GTK.Container):

    def __init__(self, maxsize=None):
        GTK.Container.__init__(self)
        self.gChildren = []
        self.maxsize = maxsize or (gdk.screen_width(), gdk.screen_height())

По умолчанию значение self.maxsize в Листинге 1 не накладывает никаких ограничений на ширину и высоту WTable.

Используемый нами метод attach имеет следующий интерфейс:

Листинг 2. Интерфейс WTable.attach
def attach(self, widget, leftright=(0,1), topbottom=(0,1), glue=None):

Обозначения leftright и topbottom схожи с используемыми в STL (Standard Template Library) полуоткрытыми промежутками и используются для столбцов и строк соответственно. Диапазоны [a,b) или (a,b] являются полуоткрытыми в том смысле, что они "открыты" на одной границе (не включают в себя граничную точку) и "закрыты" на другой (то есть включают граничную точку). Для описания параметра glue необходимо сделать пояснения по поводу того, как используются индексы (0,1).

Размерности и перечисления

Горизонтальное и вертикальное направления рассматриваются симметрично. Вместо того, чтобы дублировать код, мы используем следующие обозначения для измерений: (X, Y) = (0, 1). Мы используем их в циклах и в индексированных двумерных массивах. Также подобные перечисления (0,1) используются для отступов слева и справа, сверху и снизу, а также для соответствующих им классов Spring (подробнее будут рассмотрены позднее).

Заметим, что gtk.Requisition (объект, который содержит информацию о необходимых требованиях к пространству для виджета) использует поля width и height, а расположение определяется при помощи gtk.gdk.Rectangle (объект, содержащий данные о прямоугольнике), который обладает полями x, y, width и height, не использующими наш удобный метод индексирования.

Размещение с помощью glue

Когда WTable размещает виджет, то создается дочерний объект, который добавляется к списку gChildren, принадлежащему WTable. Дочерний объект имеет следующее определение класса:

Листинг 3. Класс Child
class Child:
    "Child of WTable. Adoption data"
    def __init__(self, w, leftright=(0,1), topbottom=(0,1), glue=None):
        self.widget = w
        # lrtb: 2x2 tuple:( Cols[begin, end), Rows[Begin, end) )
        self.lrtb = (leftright, topbottom)
        self.glue = glue

Он содержит данные, которые передаются методу attach, относящемуся к WTable.

Класс Glue учитывает оба измерения. Он состоит из двух одномерных методов Glue1D:

Листинг 4. Класс Glue
class Glue:
    "2-Dimensional Glue"
    def __init__(self, xglue=None, yglue=None):
        self.xy = (xglue or Glue1D(), yglue or Glue1D())

Каждый Glue1D содержит значение grow weight и две строки, которые описывают направление слева направо или же направление сверху вниз. Мы описываем этот тип значений как wGrow.

Листинг 5. Glue1D
class Glue1D:
    "1-Dimensional Glue"
    def __init__(self, preSpring=None, postSpring=None, wGrow=1):
        self.springs = (preSpring or Spring(), postSpring or Spring())
        self.wGrow = wGrow # of owning widget

Наконец, класс Spring содержит значения, которые описываются в показанном ниже конструкторе класса.

Листинг 6. Spring
class Spring:
    "One side attachment requirement"
    def __init__(self, pad=0, wGrow=0):
        self.pad = pad
        self.wGrow = wGrow

Итак, объект класса Glue содержит четыре объекта Spring, которые описывают размеры отступа от краев (margins' sizes). Шесть (2x(1+2)=6) значений wGrow выполняют следующие две задачи:

  • Глобально определяют дополнительное относительное увеличение пространства, которое получают столбцы или ряды
  • Для каждого прикрепленного родительского объекта локально определяют распределение дополнительного пространства между дочерним объектом и относящимися к нему (2x2=4) отступами

Для удобства мы будем использовать следующий метод "total wGrow":

Листинг 7. Glue1D.total_grow_weight
def total_grow_weight(self):
    return self.wGrow + self.springs[0].wGrow + self.springs[1].wGrow

Затем мы возвращаемся и определяем метод attach. (Заметим, что вы можете вместо диапазона столбцов и рядов указывать отдельный столбец или ряд).

Листинг 8. WTable.attach
def attach(self, widget, leftright=(0,1), topbottom=(0,1), glue=None):
    if glue == None:
        glue = Glue()
    # Для удобства преобразуем отдельное значение n в to (n,n+1)
    lrtb = map(lambda x: (type(x) == types.IntType) and (x, x+1) or x,
               (leftright, topbottom))
    child = Child(widget, lrtb[0], lrtb[1], glue)
    self.gChildren.append(child)
    if self.flags() & GTK.REALIZED:
        widget.set_parent_window(self.window)
    widget.set_parent(self)
    self.queue_resize()
    return child

Реализация

Модель управления геометрией в GTK+ состоит из двух фаз:

  • Запрос (Requisition): Во время этой фазы контейнер перемещается по списку дочерних объектов и опрашивает их, какой размер им необходим.
  • Размещение (Allocation): Во время этой фазы контейнер получает некоторый прямоугольник, вложенный в пространство пикселов, и распределяет его между дочерними объектами.

Следует понимать, что обе эти фазы являются рекурсивными, а также что некоторые из дочерних объектов могут сами являться контейнерами.

Некоторые из методов foo из gtk.Widget (базовый класс для всех виджетов PyGTK) сами вызывают виртуальные методы do_foo. На данный момент не существует ясной документации по этому вопросу, однако принцип понятен из данной статьи и примеров, которые перечислены в разделе Похожие темы. Наша реализация переопределяет некоторые из этих виртуальных методов do_foo.

Запрос (Requisition)

Фаза запроса реализована при помощи следующего метода:

Листинг 9. WTable.do_size_request
def do_size_request(self, oreq):
    reqMgrs = (req.Manager(), req.Manager()) # (X,Y)
    for child in self.gChildren:
        request = child.widget.size_request() # вычисляем!
        for xyi in (X, Y):
            be = child.lrtb[xyi]
            glue1d = child.glue.xy[xyi]
            sz = request[xyi] + glue1d.base()
            rr = req.RangeSize(be, sz)
            reqMgrs[xyi].addRangeReq(rr)
    self.reqs = map(lambda m: m.solve(), reqMgrs) # (X,Y)
    bw2 = 2 * self.border_width
    oreq.width  = min(sum(self.reqs[X]) + bw2, self.maxsize[0])
    oreq.height = min(sum(self.reqs[Y]) + bw2, self.maxsize[1])

Значения возвращаются при помощи полей oreq.width и oreq.height. Объекты reqMgrs собирают информацию о требуемых размерах, полученную при запросах к дочерним объектам, а затем вызывают метод solve() из класса req.Manager. Этот метод определяет и возвращает размер, который необходим для каждого ряда и столбца. Однако сейчас нас интересуют только суммы, которые возвращаются при помощи oreq. Заметим, что хотя мы и получаем вычисленные значения для каждого ряда и столбца, требования могут указываться и для определенного диапазона рядов и столбцов при помощи ranges. Эти диапазоны могут включать в себя более чем один ряд или столбец.

Размещение

Во время этой стадии мы получаем некоторую часть драгоценного пространства на экране дисплея. Размещение определяется результатами, полученными на предыдущей стадии запроса, а также той политикой, которая осуществляется порождающими контейнерами (одним из которых может являться и менеджер окон рабочего пространства, который выполняет необходимое пользователю изменение размеров окна верхнего уровня).

Если в процессе размещения удается в точности удовлетворить предложенным WTable требованиям, то вы просто распределяете между дочерними объектами WTable те результаты, которые req.Manager вычислил на предыдущем этапе. В противном случае вам приходится иметь дело с дополнительным пространством или, что менее вероятно, с недостаточным пространством. В любом случае необходимо каким-то образом распределить это неучтенное пространство.

Теперь в качестве псевод-требований используются значения wGrow, относящиеся к glue и виджету, однако их сбор и обработка производится аналогичным образом. Однако на этот раз единицами являются не пикселы, а относительные коэффициенты (веса), соотношения между которыми используются для расширения рядов и столбцов или же, напротив, для их уменьшения (см. Замечания).

Листинг 10. WTable.do_size_allocate
def do_size_allocate(self, allocation):
    self.allocation = allocation
    allocs = (allocation.width, allocation.height)
    alloc_offsets = (self.border_width, self.border_width)
    self.crSizes = [None, None]  # списки: размер столбца, размер ряда.
    self.offsets = [None, None]  # alloc_offsets + частиные суммы crSizes
    for xyi in (X, Y):
        a = allocs[xyi]
        reqs = self.reqs[xyi]
        gWeights = self._getGrowWeights(xyi)
        self.crSizes[xyi] = given = self._divide(allocs[xyi], reqs, gWeights)
        offsets = len(given) * [None]
        offsets[0] = alloc_offsets[xyi]
        for oi in range(1, len(offsets)):
            offsets[oi] = offsets[oi - 1] + given[oi - 1]
        self.offsets[xyi] = offsets
    for child in self.gChildren:
        self._allocate_child(child)

    if self.flags() & GTK.REALIZED:
        self.window.move_resize(*allocation)

Как показано в листинге 10, метод do_size_allocate() выполняет следующие действия:

  • Определяет размер каждого сегмента (это может быть столбец или ряд) для каждого измерения. Если сложить частичные суммы этих размеров вместе с allocation.x и allocation.y, то также можно получить и смещения.
  • После того, как определено размещение рядов и столбцов, вы приступаете к размещению каждого из дочерних объектов. Вновь заметим, что дочерний объект может занимать не только отдельную "ячейку", но и прямоугольное пространство, которое определяется при помощи ranges, указывающего диапазон используемых столбцов и рядов.

В заключение при необходимости вызывается метод window.move_resize(). Заметим, что параметр allocation обозначен символом *, что означает использование метода упорядочения, предоставленного классом gtk.gdk.Rectangle (см. элемент fields в define-boxed Rectangle, который используется для автоматической генерации C-кода, определяющего PyGdkRectangle_Type. Благодаря этому allocation преобразуется в кортеж (tuple), который согласуется с методом move_resize() класса gtk.gdk.Window.)

Распределение выделенного пространства в таблице

На предыдущей стадии запроса были определены требуемые размеры в пикселах для каждого столбца и для каждого ряда. Теперь необходимо распределить выделенное полное пространство. Если оно в точности соответствует требованиям, то вы просто используете его, если же нет - то необходимо выполнить подстройку, добавляя или (что менее вероятно) вычитая дополнительные пикселы.

Разница распределяется согласно весовым коэффициентам. Вновь пользователь не присваивает явно подобные коэффициенты столбцам и рядам, он присваивает значения wGrow каждому дочернему объекту при помощи "Glue." Это делается для того, чтобы вычислить необходимые коэффициенты при помощи класса req.Manager.

Листинг 11. WTable._getGrowWeights
def _getGrowWeights(self, xyi):
    wMgr = req.Manager()
    for child in self.gChildren:
        be = child.lrtb[xyi]
        glue1d = child.glue.xy[xyi]
        rr = req.RangeSize(be, glue1d.total_grow_weight())
        wMgr.addRangeReq(rr)
    wMgr.solve()
    gws = wMgr.reqs
    if sum(gws) == 0:
        gws = len(gws) * [1]  # если коэффициенты = 0, равные пропорции
    return gws

Если пространства для размещения хватает с избытком (Листинг 12), то вы разделяете этот избыток в соответствии с коэффициентами и требованиями. Заметим, что если образуется дефицит пространства, то вы преобразуете коэффициенты таким образом, чтобы коэффициенты с большими значениями пострадали в меньшей степени. Также необходимо учесть вариант, когда все коэффициенты равны нулю - это означает, что все они считаются равными.

Листинг 12. WTable._divide
def _divide(self, cake, requirements, growWeights):
    n = len(requirements)   # == len(growWeights)
    given = requirements[:] # начинаем с точного выполнения требований
    reqTotal = sum(requirements)
    delta = cake - reqTotal
    if delta < 0: # редко, "обращаем" коэффициенты
        growWeights = map(lambda x: max(growWeights) - x, growWeights)
        if sum(growWeights) == 0:
            growWeights = n * [1]; # выравниваем
    i = 0
    gwTotal = sum(growWeights)
    while gwTotal > 0 and delta != 0:
        add = (delta * growWeights[i] + gwTotal/2) / gwTotal
        gwTotal -= growWeights[i]
        given[i] += add
        delta -= add
        i += 1
    return given

Выделение пространства для дочерних объектов

Теперь, когда определено размещение для столбцов и рядов и оно представлено при помощи crSizes и offsets, выделение пространства для каждого из дочерних объектов является достаточно простой задачей. Единственное, что необходимо принимать во внимание - это различие между предоставленным дочернему объекту пространством и тем, что он требовал.

Листинг 13. WTable._allocate_child
def _allocate_child(self, child):
    offsetsxy = [None, None]
    req = list( child.widget.get_child_requisition() ) # pre-calculated
    for xyi in (X, Y):
        segRange = child.lrtb[xyi]
        g1d = child.glue.xy[xyi]
        supply = sum( self.crSizes[xyi][segRange[0]: segRange[1]] )
        (oadd, cadd) = g1d.place(req[xyi], supply)
        offsetsxy[xyi] = self.offsets[xyi][ segRange[0] ] + oadd
        req[xyi] += cadd
    allocation = gdk.Rectangle(x=offsetsxy[0], y=offsetsxy[1],
                               width=req[0], height=req[1])
    child.widget.size_allocate(allocation)

Чтобы определить, насколько должны увеличиваться в размерах (или уменьшаться) дочерний объект и соответствующие отступы, используется метод Glue1D.place. Для каждого из дочерних объектов данный метод вызывается дважды: по одному разу для каждого из направлений (X,Y). Для каждого направления необходимо учитывать три значения wGrow: две для сторон (левая и правая, или же верхняя и нижняя), а также значение для самого дочернего объекта.

Листинг 14. Glue1D.place
def place(self, cneed, supply):
    pads = self.base()
    need = cneed + pads
    delta = supply - need;
    if delta >= 0:
        gwTotal = self.total_grow_weight()
        oadd = self.springs[0].pad
        if gwTotal == 0:
            cadd = delta
        else:
            oadd += round_div(delta * self.springs[0].wGrow, gwTotal)
            cadd = round_div(delta * self.wGrow, gwTotal)
    else: # rare
        shrink = -delta
        if pads >= shrink:  # достаточно уменьшить в размере pads
            oadd = round_div(self.springs[0].pad * delta, pads)
            cadd = 0
        else:
            oadd = 0
            cadd = delta    # также необходимо уменьшить дочерний объект
    return (oadd, cadd)

Метод Glue1D.place возвращает кортеж (tuple) из двух целых чисел (oadd,cadd).

  • oadd добавляется к смещению. Или же можно сказать, что это значение означает, насколько будет увеличен первый (левый или верхний) отступ.
  • cadd добавляется к параметру, который определяет размеры дочернего объекта (ширина или высота).

Здесь используется упоминавшийся ранее метод total_grow_weight() и следующая тривиальная функция round_div (см. PEP 238 в Похожие темы):

Листинг 15. round_div
def round_div(n, d):
    return (n + d/2) / d

Согласование требований

Для каждого требования к размеру columns мы получаем требования к диапазону столбцов [b,e)—то есть для всех c, таких что: b <= c < e—будут иметь такие размеры, что их сумма будет равна или больше некоторого значения. Требование r представляется при помощи следующей формулы:

Рисунок 7. Требования к размеру столбцов
Columns sizes requirement

Нам необходимо найти такие "минимальные" размеры (Si), которые будут удовлетворять неравенствам для соответствующих требований. Эта проблема решается методами линейного программирования. Однако в данном случае имеются определенные ограничения, а именно:

  • Все коэффициенты имеют значения 0 или 1.
  • Все коэффициенты со значением, равным 1, относятся к последовательному диапазону переменных.
  • Нас интересует решение только в целых числах.

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

Далее будут вкратце описаны функции класса req.Manager, который обеспечивает приемлемое решение данной проблемы.Так как это не является основной темой данной статьи, и метод решения не является вполне оптимальным, подробно данные функции здесь описываться не будут.

Этот класс использует отдельный модуль req.py, который не зависит от GTK+. Модуль обладает встроенным тестом, который выполняется при помощи имеющейся в Python конструкции if __name__ == '__main__':.

Ниже приводятся некоторые примеры тестирования. В каждом случае указывается список с тремя значениями (Bi, Ei, Si), что означает, что выполняется поиск массива значений, у которого каждый поддиапазон [Bi, Ei) при суммировании дает по меньшей мере значение Si.

Листинг 16. Примеры расчетов, выполненных req.Manager
python req.py    0 1 20    1 2 30
Solution: [20, 30]

python req.py    0 2 10    1 3 5
Solution: [5, 5, 1]

python req.py    0 2 100    1 3 50
Solution: [46, 54, 6]

python req.py    0 2 100    1 3 50    2 3 20
Solution: [49, 51, 21]

Добавление требований к req.Manager

Ниже показан конструктор для класса req.Manager вместе с методом, который позволяет добавлять требование.

Листинг 17. Метод req.ManageraddRangeReq
class Manager:
    def __init__(self):
        self.rangeReqs = []
        self.reqs = None

    def addRangeReq(self, rangeReq):
        self.rangeReqs += [rangeReq]

Здесь rangeReq представляет собой объект, относящийся к следующему классу:

Листинг 18. req.RangeSize
class RangeSize:
    def __init__(self, be=(0,1), sz=1):
        self.begin = be[0]
        self.end = be[1]
        self.size = sz

Решение при помощи эвристического метода

Для поиска оптимального решения потребовалось бы использовать методы линейного программирования, при этом код был бы значительно сложнее.

Листинг 19. req.Manager.solve
def solve(self):
    n = self.nSegments()
    m = len(self.rangeReqs)
    self.reqs = n * [0]
    self.rangeReqs.sort()

    # Выполняем требования для одного сегмента
    dumSingle = RangeSize((n, n+1), 1)
    endSingleIndex = bisect.bisect_right(self.rangeReqs, dumSingle)
    for rr in self.rangeReqs[ : endSingleIndex]:
        curr = self.reqs[rr.begin]
        needMore = rr.size - curr
        self.reqs[rr.begin] += needMore
    bigRangeReqs = self.rangeReqs[endSingleIndex:] # диапазоны из нескольких требований

    self.partialSatisfy(bigRangeReqs, 1, 2) # половина
    self.partialSatisfy(bigRangeReqs, 1, 2) # половина
    self.partialSatisfy(bigRangeReqs, 1, 1) # полностью
    return self.reqs

Приведенный выше листинг 19 содержит метод solve() и использует простой эвристический подход, который основывается на следующем:

  1. Сортировка требований.
  2. Выполнение требований для одного элемента для диапазонов [b,e), при этом b+1=e.
  3. Для каждого невыполненного требования r добавляется половина не выделенного для r размера, который равномерно распределяются среди элементов, относящихся к диапазону r. Это осуществляется при помощи вызова метода req.Manager.partialSatisfy. Этот шаг выполняется дважды.
  4. Как и на предыдущем этапе, теперь добавляется полностью весь невыделенный размер.
Листинг 20. req.Manager.partialSatisfy
def partialSatisfy(self, bigRangeReqs, rationN, ratioD):
    # В reqs добвляем для выполнения требований rationN/ratioD
    for rr in bigRangeReqs:
        curr = sum(self.reqs[rr.begin: rr.end])
        needMore = rr.size - curr
        if needMore > 0:
            give = (rationN * (needMore + ratioD - 1)) / ratioD
            q, r = divmod(give, rr.end - rr.begin)
            for si in range(rr.begin, rr.end):
                self.reqs[si] += q
            for si in range(rr.begin, rr.begin + r):
                self.reqs[si] += 1

Интересно сравнить наш код и С-код функции gtk_table_size_request из GTK+, который выглядит следующим образом:

Листинг 21. Фрагмент кода из gtk_table_size_request
GTK_table_size_request_pass1 (table);
GTK_table_size_request_pass2 (table);
GTK_table_size_request_pass3 (table);
GTK_table_size_request_pass2 (table);

а также посмотреть, как реализованы функции gtk_table_size_request_pass {1,2,3}.

Тестирование

Представленный в данной статье пакет сопровождается тестовой программой wtbl-test.py, которая выполняет следующие основные действия:

  • Создает gtk.ToggleButton и Glue при помощи GUI и использует их для вызова WTable.attach().
  • Модифицирует и удаляет дочерние объекты и объекты glue, относящиеся к WTable.
  • Загружает определения для gtk.ToggleButtons и Glue из текстового файла, выполняет их создание и прикрепление. Все это использовалось при создании примеров для "Размещение в таблице", которые приводились ранее в данной статье.
  • Сохраняет существующие дочерние объекты и соответствующие объекты glue в текстовом файле в формате, который обеспечивает возможность последующей загрузки.

В дополнение к WTable, которая редактируется пользователем, в управляющем окне тестовой программы также используются два экземпляра WTable с настройками glue по умолчанию. Это демонстрирует, что реализация класса WTable не имеет глобальных зависимостей.


Ресурсы для скачивания


Похожие темы

  • Оригинал статьи: Manage widget geometry in PyGTK (EN).
  • В статье Создаем собственный виджет с помощью PyGTK, автором которой является Марк Мрасс, содержится дополнительный материал. (EN)
  • Если необходимо создать на Python контейнер с функцией прокрутки, то подобный пример содержится в статье, автором которой является Йохан Далин.(EN)
  • Онлайновая книга GTK+/Gnome Application Development содержит превосходное описание разработки приложений на GTK+, а также работы с контейнерами и виджетами.(EN)
  • В серии Основы GTK+ (developerWorks, декабрь 2005—март 2006) описан пример приложения Hello World, созданного с помощью PyGTK (см. часть 2):
    • "Part 1: Why use GTK+?" представляет собой введение в мир GTK+ и рассказывает о данной среде, об особенностях ее использования и о тех преимуществах, которые она предоставляет.
    • "Part 2: How to use GTK+" содержит начальные сведения по программированию с помощью GTK+.
    • "Part 3: How to deploy GTK+" содержит всю необходимую информацию о том, как доставить созданный вами продукт потребителю.
  • В статье "Changing the Division Operator (PEP 238)" подробно рассказывается о том, как исправить существующую в Python неоднозначность при использовании численных аргументов в операции деления. (EN)
  • Линейное программирование достаточно хорошо описывается в Wikipedia.(EN)
  • Статья STL Links & Quick Reference, автором которой является Йотам, содержит базовую информацию по STL, включая и его 8-страничную статью "Quick Reference to STL."(EN)
  • Созданный Йотамом курс образовательный курс по Linux на иврите используется в демократической школе Кешет.(EN)
  • В статье "Sugar, the XO laptop, and One Laptop per Child" (developerWorks, апрель 2007) рассказывается о Sugar, графичеком интерфейсе, созданном при помощи PyGTK.(EN)
  • Сайт проекта GTK+ содержит множество ресурсов для GTK+, который является удобным и функциональным инструментом для создания графических интерфейсов пользователя, обладает кросс-платформенной совместимостью и набором простых в использовании функций API.(EN)
  • PyGTK представляет собой реализацию GTK+ для Python; этот инструмент позволяет легко создавать программы с поддержкой графического интерфейса пользователя на основе языка программирования Python. (EN)
  • Раздел Linux сайта developerWorks, содержит дополнительные ресурсы для разработчиков, использующих Linux; также советуем просмотреть наши наиболее популярные статьи и учебные материалы.(EN)
  • Взгляните на советы и учебные пособия по Linux на developerWorks.
  • Разработайте ваш следующий Linux-проект с помощью ознакомительного ПО IBM, которое можно загрузить прямо с developerWorks. (EN)

Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux
ArticleID=381952
ArticleTitle=Управление геометрией виджетов в PyGTK
publish-date=04142009