IBM®
Перейти к тексту
    в России и странах СНГ [изменить]    Условия использования
 
 
   
    Главная страница    Продукты    Услуги и решения    Поддержка и загрузка    Мой профиль    
Перейти к тексту

developerWorks Россия  >  Linux  >

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

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

developerWorks
Опции документа
PDF format - Fits A4 and Letter

PDF - Fits A4 and Letter
98KB (17 страница)

Загрузить Adobe® Reader®

Опции документа, требующие включения JavaScript, не отображаются

Обсудить

Исходные тексты примера


Выскажите мнение об этой странице

Помогите нам улучшить содержание


Уровень сложности: средний

Йотам Медини, инженер-программист, Jungo Ltd.

14.04.2009

В GTK+ существует несколько виджетов-контейнеров, а API этого инструментария позволяет пользователю создавать собственные контейнеры. Этот API также доступны в PyGTK. В данной статье вы узнаете, как создавать "weighted-table" контейнеры в PyGTK. Этот метод показывает использование основной модели управления геометрией в GTK+ и дает представление о том, что следует делать и чего ожидать при реализации виджетов-контейнеров.

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

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

Кто является основными игроками?
GTK+ является очень удобным и функциональным инструментом для создания графических пользовательских интерфейсов, поддерживает кросс-платформенность и имеет простой в использовании API. GTK+ создан на C, однако может использоваться и с многими другими популярными языками программирования, такими как C++, Python и C#.

PyGTK представляет собой версию GTK+ для Python. Он позволяет без особых усилий создавать программы на языке Python с поддержкой графического пользовательского интерфейса.

К наиболее распространенным виджетам, которые выполняют функции выравнивания, относятся gtk.HBox, который используется для выравнивания в горизонтальном направлении, и gtk.VBox, который используется для выравнивания в вертикальном направлении.

Если необходим виджет, который работает в двух измерениях, существует gtk.Table.

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

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

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

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


    Рисунок 1. Одна кнопка на маленькой форме
    Single button within a small space



    Рисунок 2. Одна кнопка на форме большего размера
    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

  3. Далее мы рассмотрим требования к расположению в нескольких столбцах, когда центральная кнопка "middle7/8" занимает 7/8 от возможных дополнительных горизонтальных пикселов.


    Рисунок 5. Требования к расположению в столбцах на небольшом пространстве
    Multi-columns requirements within a small space



    Рисунок 6. Требования к расположению в столбцах на увеличенном пространстве
    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. На данный момент не существует ясной документации по этому вопросу, однако принцип понятен из данной статьи и примеров, которые перечислены в разделе Resources. Наша реализация переопределяет некоторые из этих виртуальных методов 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])

Замечания
Так как наша задача - дать представление об используемом методе, мы опускаем многие возможные улучшения. Например, не используется проверка параметров (parameter validation) и стандартная для Python строка, документирующая методы.

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

Объекты Glue могут безопасно использоваться совместно, так как WTable не включает их и не изменяет их значения.

Значения коэффициентов wGrows в основном используются для расширения при появлении дополнительного пространства. Для тех редких случаев, когда пространства не хватает, желательно, чтобы элементы с большими значениями wGrow уменьшались в размерах в меньшей степени. Итак, эти значения будут использоваться и в методе уменьшения размеров. Более продвинутая реализация Glue может использовать вместо этого явные значения для коэффициентов уменьшения.

Значения возвращаются при помощи полей 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 в Resources):


Листинг 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 не имеет глобальных зависимостей.




В начало


Загрузка

ОписаниеИмяРазмерМетод загрузки
Файлы примеров для данной статьиwtbl-2008-05-02-1101.tgz35 КБHTTP
Информация о методах загрузки


Ресурсы

Научиться

Получить продукты и технологии
  • Сайт проекта GTK+ содержит множество ресурсов для GTK+, который является удобным и функциональным инструментом для создания графических интерфейсов пользователя, обладает кросс-платформенной совместимостью и набором простых в использовании функций API.(EN)

  • PyGTK представляет собой реализацию GTK+ для Python; этот инструмент позволяет легко создавать программы с поддержкой графического интерфейса пользователя на основе языка программирования Python. (EN)

  • Закажите SEK для Linux, набор из двух DVD с новейшими ознакомительными версиями ПО от IBM: DB2®, Lotus®, Rational®, Tivoli® и WebSphere®. (EN)

  • Разработайте ваш следующий Linux-проект с помощью ознакомительного ПО IBM, которое можно загрузить прямо с developerWorks. (EN)


Обсудить


Об авторе

Йотам Медини (Yotam Medini) работает инженером по разработке программного обеспечения на протяжении более чем 20 лет. Он начинал с Паскаля, однако вскоре перешел на C и C++. Там, где необходимо быстро создать решение, и везде, где только это возможно, он использует Python. В настоящее время он работает в компании Jungo Ltd., г. Нетанья, Израиль. Также он в качестве добровольца преподает программирование на Python в операционной системе Linux (на иврите) в демократической школе Кешет.




Выскажите мнение об этой странице


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



 


 


 


Поделиться этой статьей:

забобрить забобрить memori сохранить в memori




В начало


IBM, логотип IBM, ibm.com, DB2, developerWorks, Lotus, Rational, Tivoli и WebSphere являются товарными знаками или зарегистрированными товарными знаками International Business Machines Corporation в США и/или других странах. Эти и другие товарные знаки IBM при первом упоминании в данном материале помечаются соответствующим символом (® или ™), указывающим на наличие зарегистрированных в США или охраняемых нормами общего права товарных знаков на момент публикации данного материала. Такие товарные знаки также могут быть зарегистрированными или охраняемыми нормами общего права товарными знаками в других странах. См. текущий перечень товарных знаков IBM. Linux является товарным знаком, принадлежащим Линусу Торвальдсу, в США и/или других странах. Другая компания, продукт или название услуги могут быть торговыми марками или знаками обслуживания, принадлежащими иным физическим или юридическим лицам.

IBM обладает всеми авторскими правами касательно информации, расположенной на developerWorks. Использование информации приведенной на этом ресурсе без явного письменного разрешения от IBM или первоначального автора запрещены. Если Вы желаете использовать информацию с developerWorks, пожалуйста воспользуйтесь регистрационной формой для того, чтобы связаться с нами запрос на использование материалов developerWorks Россия.
    IBM в России Конфиденциальность Контакты