PmwPanedWidget.py :  » Development » Leo » Leo-4.7.1-final » leo » extensions » Pmw » Pmw_1_3 » lib » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » Leo 
Leo » Leo 4.7.1 final » leo » extensions » Pmw » Pmw_1_3 » lib » PmwPanedWidget.py
# PanedWidget
# a frame which may contain several resizable sub-frames

import string
import sys
import types
import Tkinter
import Pmw

class PanedWidget(Pmw.MegaWidget):

    def __init__(self, parent = None, **kw):

  # Define the megawidget options.
  INITOPT = Pmw.INITOPT
  optiondefs = (
            ('command',            None,         None),
            ('orient',             'vertical',   INITOPT),
            ('separatorrelief',    'sunken',     INITOPT),
            ('separatorthickness', 2,            INITOPT),
            ('handlesize',         8,            INITOPT),
            ('hull_width',         400,          None),
            ('hull_height',        400,          None),
  )
  self.defineoptions(kw, optiondefs,
                dynamicGroups = ('Frame', 'Separator', 'Handle'))

  # Initialise the base class (after defining the options).
  Pmw.MegaWidget.__init__(self, parent)

  self.bind('<Configure>', self._handleConfigure)

  if self['orient'] not in ('horizontal', 'vertical'):
      raise ValueError, 'bad orient option ' + repr(self['orient']) + \
    ': must be either \'horizontal\' or \'vertical\''

        self._separatorThickness = self['separatorthickness']
        self._handleSize = self['handlesize']
  self._paneNames = []            # List of pane names
  self._paneAttrs = {}            # Map from pane name to pane info

  self._timerId = None
  self._frame = {}
  self._separator = []
  self._button = []
  self._totalSize = 0
  self._movePending = 0
  self._relsize = {}
  self._relmin = {}
  self._relmax = {}
  self._size = {}
  self._min = {}
  self._max = {}
  self._rootp = None
  self._curSize = None
  self._beforeLimit = None
  self._afterLimit = None
  self._buttonIsDown = 0
  self._majorSize = 100
  self._minorSize = 100

  # Check keywords and initialise options.
  self.initialiseoptions()

    def insert(self, name, before = 0, **kw):
  # Parse <kw> for options.
        self._initPaneOptions(name)
  self._parsePaneOptions(name, kw)

  insertPos = self._nameToIndex(before)
  atEnd = (insertPos == len(self._paneNames))

  # Add the frame.
  self._paneNames[insertPos:insertPos] = [name]
  self._frame[name] = self.createcomponent(name,
    (), 'Frame',
    Tkinter.Frame, (self.interior(),))

  # Add separator, if necessary.
  if len(self._paneNames) > 1:
      self._addSeparator()
  else:
      self._separator.append(None)
      self._button.append(None)

  # Add the new frame and adjust the PanedWidget
  if atEnd:
      size = self._size[name]
      if size > 0 or self._relsize[name] is not None:
    if self['orient'] == 'vertical':
        self._frame[name].place(x=0, relwidth=1,
              height=size, y=self._totalSize)
    else:
        self._frame[name].place(y=0, relheight=1,
              width=size, x=self._totalSize)
      else:
    if self['orient'] == 'vertical':
        self._frame[name].place(x=0, relwidth=1,
              y=self._totalSize)
    else:
        self._frame[name].place(y=0, relheight=1,
              x=self._totalSize)
  else:
      self._updateSizes()

  self._totalSize = self._totalSize + self._size[name]
  return self._frame[name]

    def add(self, name, **kw):
        return apply(self.insert, (name, len(self._paneNames)), kw)

    def delete(self, name):
  deletePos = self._nameToIndex(name)
  name = self._paneNames[deletePos]
  self.destroycomponent(name)
  del self._paneNames[deletePos]
  del self._frame[name]
  del self._size[name]
  del self._min[name]
  del self._max[name]
  del self._relsize[name]
  del self._relmin[name]
  del self._relmax[name]

  last = len(self._paneNames)
  del self._separator[last]
  del self._button[last]
        if last > 0:
            self.destroycomponent(self._sepName(last))
            self.destroycomponent(self._buttonName(last))

  self._plotHandles()

    def setnaturalsize(self):
        self.update_idletasks()
        totalWidth = 0
        totalHeight = 0
        maxWidth = 0
        maxHeight = 0
  for name in self._paneNames:
            frame = self._frame[name]
            w = frame.winfo_reqwidth()
            h = frame.winfo_reqheight()
            totalWidth = totalWidth + w
            totalHeight = totalHeight + h
            if maxWidth < w:
                maxWidth = w
            if maxHeight < h:
                maxHeight = h

        # Note that, since the hull is a frame, the width and height
        # options specify the geometry *outside* the borderwidth and
        # highlightthickness.
        bw = string.atoi(str(self.cget('hull_borderwidth')))
        hl = string.atoi(str(self.cget('hull_highlightthickness')))
        extra = (bw + hl) * 2
        if str(self.cget('orient')) == 'horizontal':
            totalWidth = totalWidth + extra
            maxHeight = maxHeight + extra
            self.configure(hull_width = totalWidth, hull_height = maxHeight)
        else:
            totalHeight = (totalHeight + extra +
                    (len(self._paneNames) - 1) * self._separatorThickness)
            maxWidth = maxWidth + extra
            self.configure(hull_width = maxWidth, hull_height = totalHeight)

    def move(self, name, newPos, newPosOffset = 0):

        # see if we can spare ourselves some work
        numPanes = len(self._paneNames)
        if numPanes < 2:
            return

        newPos = self._nameToIndex(newPos) + newPosOffset
        if newPos < 0 or newPos >=numPanes:
            return

        deletePos = self._nameToIndex(name)

        if deletePos == newPos:
            # inserting over ourself is a no-op
            return

        # delete name from old position in list
        name = self._paneNames[deletePos]
        del self._paneNames[deletePos]

        # place in new position
        self._paneNames[newPos:newPos] = [name]

        # force everything to redraw
        self._plotHandles()
        self._updateSizes()

    def _nameToIndex(self, nameOrIndex):
  try:
      pos = self._paneNames.index(nameOrIndex)
  except ValueError:
      pos = nameOrIndex

  return pos

    def _initPaneOptions(self, name):
  # Set defaults.
  self._size[name] = 0
  self._relsize[name] = None
  self._min[name] = 0
  self._relmin[name] = None
  self._max[name] = 100000
  self._relmax[name] = None

    def _parsePaneOptions(self, name, args):
  # Parse <args> for options.
  for arg, value in args.items():
      if type(value) == types.FloatType:
    relvalue = value
    value = self._absSize(relvalue)
      else:
    relvalue = None

      if arg == 'size':
    self._size[name], self._relsize[name] = value, relvalue
      elif arg == 'min':
    self._min[name], self._relmin[name] = value, relvalue
      elif arg == 'max':
    self._max[name], self._relmax[name] = value, relvalue
      else:
    raise ValueError, 'keyword must be "size", "min", or "max"'

    def _absSize(self, relvalue):
  return int(round(relvalue * self._majorSize))

    def _sepName(self, n):
  return 'separator-%d' % n

    def _buttonName(self, n):
  return 'handle-%d' % n

    def _addSeparator(self):
  n = len(self._paneNames) - 1

  downFunc = lambda event, s = self, num=n: s._btnDown(event, num)
  upFunc = lambda event, s = self, num=n: s._btnUp(event, num)
  moveFunc = lambda event, s = self, num=n: s._btnMove(event, num)

  # Create the line dividing the panes.
  sep = self.createcomponent(self._sepName(n),
    (), 'Separator',
    Tkinter.Frame, (self.interior(),),
    borderwidth = 1,
    relief = self['separatorrelief'])
  self._separator.append(sep)

  sep.bind('<ButtonPress-1>', downFunc)
  sep.bind('<Any-ButtonRelease-1>', upFunc)
  sep.bind('<B1-Motion>', moveFunc)

  if self['orient'] == 'vertical':
      cursor = 'sb_v_double_arrow'
      sep.configure(height = self._separatorThickness,
                    width = 10000, cursor = cursor)
  else:
      cursor = 'sb_h_double_arrow'
      sep.configure(width = self._separatorThickness,
                    height = 10000, cursor = cursor)

  self._totalSize = self._totalSize + self._separatorThickness

  # Create the handle on the dividing line.
  handle = self.createcomponent(self._buttonName(n),
    (), 'Handle',
    Tkinter.Frame, (self.interior(),),
        relief = 'raised',
        borderwidth = 1,
        width = self._handleSize,
        height = self._handleSize,
        cursor = cursor,
    )
  self._button.append(handle)

  handle.bind('<ButtonPress-1>', downFunc)
  handle.bind('<Any-ButtonRelease-1>', upFunc)
  handle.bind('<B1-Motion>', moveFunc)

  self._plotHandles()

  for i in range(1, len(self._paneNames)):
      self._separator[i].tkraise()
  for i in range(1, len(self._paneNames)):
      self._button[i].tkraise()

    def _btnUp(self, event, item):
  self._buttonIsDown = 0
  self._updateSizes()
  try:
      self._button[item].configure(relief='raised')
  except:
      pass

    def _btnDown(self, event, item):
  self._button[item].configure(relief='sunken')
  self._getMotionLimit(item)
  self._buttonIsDown = 1
  self._movePending = 0

    def _handleConfigure(self, event = None):
  self._getNaturalSizes()
  if self._totalSize == 0:
      return

  iterRange = list(self._paneNames)
  iterRange.reverse()
  if self._majorSize > self._totalSize:
      n = self._majorSize - self._totalSize
      self._iterate(iterRange, self._grow, n)
  elif self._majorSize < self._totalSize:
      n = self._totalSize - self._majorSize
      self._iterate(iterRange, self._shrink, n)

  self._plotHandles()
  self._updateSizes()

    def _getNaturalSizes(self):
  # Must call this in order to get correct winfo_width, winfo_height
  self.update_idletasks()

  self._totalSize = 0

  if self['orient'] == 'vertical':
      self._majorSize = self.winfo_height()
      self._minorSize = self.winfo_width()
      majorspec = Tkinter.Frame.winfo_reqheight
  else:
      self._majorSize = self.winfo_width()
      self._minorSize = self.winfo_height()
      majorspec = Tkinter.Frame.winfo_reqwidth

        bw = string.atoi(str(self.cget('hull_borderwidth')))
        hl = string.atoi(str(self.cget('hull_highlightthickness')))
        extra = (bw + hl) * 2
        self._majorSize = self._majorSize - extra
        self._minorSize = self._minorSize - extra

  if self._majorSize < 0:
      self._majorSize = 0
  if self._minorSize < 0:
      self._minorSize = 0

  for name in self._paneNames:
      # adjust the absolute sizes first...
      if self._relsize[name] is None:
    #special case
    if self._size[name] == 0:
        self._size[name] = apply(majorspec, (self._frame[name],))
        self._setrel(name)
      else:
    self._size[name] = self._absSize(self._relsize[name])

      if self._relmin[name] is not None:
    self._min[name] = self._absSize(self._relmin[name])
      if self._relmax[name] is not None:
    self._max[name] = self._absSize(self._relmax[name])

      # now adjust sizes
      if self._size[name] < self._min[name]:
    self._size[name] = self._min[name]
    self._setrel(name)

      if self._size[name] > self._max[name]:
    self._size[name] = self._max[name]
    self._setrel(name)

      self._totalSize = self._totalSize + self._size[name]

  # adjust for separators
  self._totalSize = (self._totalSize +
                (len(self._paneNames) - 1) * self._separatorThickness)

    def _setrel(self, name):
  if self._relsize[name] is not None:
      if self._majorSize != 0:
    self._relsize[name] = round(self._size[name]) / self._majorSize

    def _iterate(self, names, proc, n):
  for i in names:
      n = apply(proc, (i, n))
      if n == 0:
    break

    def _grow(self, name, n):
  canGrow = self._max[name] - self._size[name]

  if canGrow > n:
      self._size[name] = self._size[name] + n
      self._setrel(name)
      return 0
  elif canGrow > 0:
      self._size[name] = self._max[name]
      self._setrel(name)
      n = n - canGrow

  return n

    def _shrink(self, name, n):
  canShrink = self._size[name] - self._min[name]

  if canShrink > n:
      self._size[name] = self._size[name] - n
      self._setrel(name)
      return 0
  elif canShrink > 0:
      self._size[name] = self._min[name]
      self._setrel(name)
      n = n - canShrink

  return n

    def _updateSizes(self):
  totalSize = 0

  for name in self._paneNames:
      size = self._size[name]
      if self['orient'] == 'vertical':
    self._frame[name].place(x = 0, relwidth = 1,
          y = totalSize,
          height = size)
      else:
    self._frame[name].place(y = 0, relheight = 1,
          x = totalSize,
          width = size)

      totalSize = totalSize + size + self._separatorThickness

  # Invoke the callback command
  cmd = self['command']
  if callable(cmd):
      cmd(map(lambda x, s = self: s._size[x], self._paneNames))

    def _plotHandles(self):
  if len(self._paneNames) == 0:
      return

  if self['orient'] == 'vertical':
      btnp = self._minorSize - 13
  else:
      h = self._minorSize

      if h > 18:
    btnp = 9
      else:
    btnp = h - 9

  firstPane = self._paneNames[0]
  totalSize = self._size[firstPane]

  first = 1
  last = len(self._paneNames) - 1

  # loop from first to last, inclusive
  for i in range(1, last + 1):

      handlepos = totalSize - 3
      prevSize = self._size[self._paneNames[i - 1]]
      nextSize = self._size[self._paneNames[i]]

      offset1 = 0

      if i == first:
    if prevSize < 4:
        offset1 = 4 - prevSize
      else:
    if prevSize < 8:
        offset1 = (8 - prevSize) / 2

      offset2 = 0

      if i == last:
    if nextSize < 4:
        offset2 = nextSize - 4
      else:
    if nextSize < 8:
        offset2 = (nextSize - 8) / 2

      handlepos = handlepos + offset1

      if self['orient'] == 'vertical':
    height = 8 - offset1 + offset2

    if height > 1:
        self._button[i].configure(height = height)
        self._button[i].place(x = btnp, y = handlepos)
    else:
        self._button[i].place_forget()

    self._separator[i].place(x = 0, y = totalSize,
           relwidth = 1)
      else:
    width = 8 - offset1 + offset2

    if width > 1:
        self._button[i].configure(width = width)
        self._button[i].place(y = btnp, x = handlepos)
    else:
        self._button[i].place_forget()

    self._separator[i].place(y = 0, x = totalSize,
           relheight = 1)

      totalSize = totalSize + nextSize + self._separatorThickness

    def pane(self, name):
  return self._frame[self._paneNames[self._nameToIndex(name)]]

    # Return the name of all panes
    def panes(self):
  return list(self._paneNames)

    def configurepane(self, name, **kw):
  name = self._paneNames[self._nameToIndex(name)]
  self._parsePaneOptions(name, kw)
  self._handleConfigure()

    def updatelayout(self):
  self._handleConfigure()

    def _getMotionLimit(self, item):
  curBefore = (item - 1) * self._separatorThickness
  minBefore, maxBefore = curBefore, curBefore

  for name in self._paneNames[:item]:
      curBefore = curBefore + self._size[name]
      minBefore = minBefore + self._min[name]
      maxBefore = maxBefore + self._max[name]

  curAfter = (len(self._paneNames) - item) * self._separatorThickness
  minAfter, maxAfter = curAfter, curAfter
  for name in self._paneNames[item:]:
      curAfter = curAfter + self._size[name]
      minAfter = minAfter + self._min[name]
      maxAfter = maxAfter + self._max[name]

  beforeToGo = min(curBefore - minBefore, maxAfter - curAfter)
  afterToGo = min(curAfter - minAfter, maxBefore - curBefore)

  self._beforeLimit = curBefore - beforeToGo
  self._afterLimit = curBefore + afterToGo
  self._curSize = curBefore

  self._plotHandles()

    # Compress the motion so that update is quick even on slow machines
    #
    # theRootp = root position (either rootx or rooty)
    def _btnMove(self, event, item):
  self._rootp = event

  if self._movePending == 0:
      self._timerId = self.after_idle(
        lambda s = self, i = item: s._btnMoveCompressed(i))
      self._movePending = 1

    def destroy(self):
        if self._timerId is not None:
          self.after_cancel(self._timerId)
    self._timerId = None
        Pmw.MegaWidget.destroy(self)

    def _btnMoveCompressed(self, item):
  if not self._buttonIsDown:
      return

  if self['orient'] == 'vertical':
      p = self._rootp.y_root - self.winfo_rooty()
  else:
      p = self._rootp.x_root - self.winfo_rootx()

  if p == self._curSize:
      self._movePending = 0
      return

  if p < self._beforeLimit:
      p = self._beforeLimit

  if p >= self._afterLimit:
      p = self._afterLimit

  self._calculateChange(item, p)
  self.update_idletasks()
  self._movePending = 0

    # Calculate the change in response to mouse motions
    def _calculateChange(self, item, p):

  if p < self._curSize:
      self._moveBefore(item, p)
  elif p > self._curSize:
      self._moveAfter(item, p)

  self._plotHandles()

    def _moveBefore(self, item, p):
  n = self._curSize - p

  # Shrink the frames before
  iterRange = list(self._paneNames[:item])
  iterRange.reverse()
  self._iterate(iterRange, self._shrink, n)

  # Adjust the frames after
  iterRange = self._paneNames[item:]
  self._iterate(iterRange, self._grow, n)

  self._curSize = p

    def _moveAfter(self, item, p):
  n = p - self._curSize

  # Shrink the frames after
  iterRange = self._paneNames[item:]
  self._iterate(iterRange, self._shrink, n)

  # Adjust the frames before
  iterRange = list(self._paneNames[:item])
  iterRange.reverse()
  self._iterate(iterRange, self._grow, n)

  self._curSize = p
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.