Added wxPython.lib.gridmovers by Gerrit van Dyk which facilitates the

dragging of columns and/or rows in a wxGrid.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_4_BRANCH@18217 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2002-12-13 23:35:43 +00:00
parent 26e3a24354
commit 2466a3a892
4 changed files with 623 additions and 6 deletions

View File

@@ -62,6 +62,10 @@ Added wxPython.lib.popupctl by Gerrit van Dyk which is a combobox-like
gizmo for poping up arbitrary controls. It is currently using
wxDialog because of some issues with wxPopupWindow...
Added wxPython.lib.gridmovers by Gerrit van Dyk which facilitates the
dragging of columns and/or rows in a wxGrid.
2.3.3.1

View File

@@ -0,0 +1,193 @@
from wxPython.wx import *
from wxPython.grid import *
from wxPython.lib.gridmovers import wxGridColMover, EVT_GRID_COL_MOVE
from wxPython.lib.gridmovers import wxGridRowMover, EVT_GRID_ROW_MOVE
#---------------------------------------------------------------------------
class CustomDataTable(wxPyGridTableBase):
"""
"""
def __init__(self, log):
wxPyGridTableBase.__init__(self)
self.log = log
self.identifiers = ['id','ds','sv','pr','pl','op','fx','ts']
self.rowLabels = ['Row1','Row2','Row3']
self.colLabels = {'id':'ID','ds':'Description','sv':'Severity',
'pr':'Priority','pl':'Platform','op':'Opened?',
'fx':'Fixed?','ts':'Tested?'}
self.data = [{'id':1010,
'ds':"The foo doesn't bar",
'sv':"major",
'pr':1,
'pl':'MSW',
'op':1,
'fx':1,
'ts':1
},
{'id':1011,
'ds':"I've got a wicket in my wocket",
'sv':"wish list",
'pr':2,
'pl':'other',
'op':0,
'fx':0,
'ts':0
},
{'id':1012,
'ds':"Rectangle() returns a triangle",
'sv':"critical",
'pr':5,
'pl':'all',
'op':0,
'fx':0,
'ts':0
}
]
#--------------------------------------------------
# required methods for the wxPyGridTableBase interface
def GetNumberRows(self):
return len(self.data)
def GetNumberCols(self):
return len(self.identifiers)
def IsEmptyCell(self, row, col):
id = self.identifiers[col]
return not self.data[row][id]
def GetValue(self, row, col):
id = self.identifiers[col]
return self.data[row][id]
def SetValue(self, row, col, value):
id = self.identifiers[col]
self.data[row][id] = value
#--------------------------------------------------
# Some optional methods
# Called when the grid needs to display column labels
def GetColLabelValue(self, col):
id = self.identifiers[col]
return self.colLabels[id]
# Called when the grid needs to display row labels
def GetRowLabelValue(self,row):
return self.rowLabels[row]
#--------------------------------------------------
# Methods added for demo purposes.
# The physical moving of the cols/rows is left to the implementer.
# Because of the dynamic nature of a wxGrid the physical moving of
# columns differs from implementation to implementation
# Move the column
def MoveColumn(self,frm,to):
grid = self.GetView()
if grid:
# Move the identifiers
old = self.identifiers[frm]
del self.identifiers[frm]
if to > frm:
self.identifiers.insert(to-1,old)
else:
self.identifiers.insert(to,old)
# Notify the grid
grid.BeginBatch()
msg = wxGridTableMessage(self,wxGRIDTABLE_NOTIFY_COLS_DELETED,
frm,1)
grid.ProcessTableMessage(msg)
msg = wxGridTableMessage(self,wxGRIDTABLE_NOTIFY_COLS_INSERTED,
to,1)
grid.ProcessTableMessage(msg)
grid.EndBatch()
# Move the row
def MoveRow(self,frm,to):
grid = self.GetView()
if grid:
# Move the rowLabels and data rows
oldLabel = self.rowLabels[frm]
oldData = self.data[frm]
del self.rowLabels[frm]
del self.data[frm]
if to > frm:
self.rowLabels.insert(to-1,oldLabel)
self.data.insert(to-1,oldData)
else:
self.rowLabels.insert(to,oldLabel)
self.data.insert(to,oldData)
# Notify the grid
grid.BeginBatch()
msg = wxGridTableMessage(self,wxGRIDTABLE_NOTIFY_ROWS_DELETED,
frm,1)
grid.ProcessTableMessage(msg)
msg = wxGridTableMessage(self,wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
to,1)
grid.ProcessTableMessage(msg)
grid.EndBatch()
#---------------------------------------------------------------------------
class DragableGrid(wxGrid):
def __init__(self, parent, log):
wxGrid.__init__(self, parent, -1)
table = CustomDataTable(log)
# The second parameter means that the grid is to take ownership of the
# table and will destroy it when done. Otherwise you would need to keep
# a reference to it and call it's Destroy method later.
self.SetTable(table, true)
# Enable Column moving
wxGridColMover(self)
EVT_GRID_COL_MOVE(self,self.GetId(),self.OnColMove)
# Enable Row moving
wxGridRowMover(self)
EVT_GRID_ROW_MOVE(self,self.GetId(),self.OnRowMove)
# Event method called when a column move needs to take place
def OnColMove(self,evt):
frm = evt.GetMoveColumn() # Column being moved
to = evt.GetBeforeColumn() # Before which column to insert
self.GetTable().MoveColumn(frm,to)
# Event method called when a row move needs to take place
def OnRowMove(self,evt):
frm = evt.GetMoveRow() # Row being moved
to = evt.GetBeforeRow() # Before which row to insert
self.GetTable().MoveRow(frm,to)
#---------------------------------------------------------------------------
class TestFrame(wxFrame):
def __init__(self, parent, log):
wxFrame.__init__(self, parent, -1, "Custom Table, data driven Grid Demo", size=(640,480))
grid = DragableGrid(self, log)
#---------------------------------------------------------------------------
if __name__ == '__main__':
import sys
app = wxPySimpleApp()
frame = TestFrame(None, sys.stdout)
frame.Show(true)
app.MainLoop()
#---------------------------------------------------------------------------

View File

@@ -4,12 +4,13 @@ from wxPython.wx import *
#---------------------------------------------------------------------------
buttonDefs = {
814 : ('GridSimple', 'Simple wxGrid, catching all events'),
815 : ('GridStdEdRend', 'wxGrid showing Editors and Renderers'),
818 : ('GridHugeTable', 'A wxGrid with a HUGE table (100 MILLION cells!)'),
817 : ('GridCustTable', 'wxGrid using a custom Table, with non-string data'),
819 : ('GridEnterHandler','Remapping keys to behave differently'),
820 : ('GridCustEditor', 'Shows how to create a custom Cell Editor'),
814 : ('GridSimple', ' Simple wxGrid, catching all events '),
815 : ('GridStdEdRend', ' wxGrid showing Editors and Renderers '),
818 : ('GridHugeTable', ' A wxGrid with a HUGE table (100 MILLION cells!) '),
817 : ('GridCustTable', ' wxGrid using a custom Table, with non-string data '),
819 : ('GridEnterHandler',' Remapping keys to behave differently '),
820 : ('GridCustEditor', ' Shows how to create a custom Cell Editor '),
821 : ('GridDragable', ' A wxGrid with dragable rows and columns '),
}

View File

@@ -0,0 +1,419 @@
#----------------------------------------------------------------------------
# Name: GridColMover.py
# Purpose: Grid Column Mover Extension
#
# Author: Gerrit van Dyk (email: gerritvd@decillion.net)
#
# Version 0.1
# Date: Nov 19, 2002
# RCS-ID: $Id$
# Licence: wxWindows license
#----------------------------------------------------------------------------
from wxPython.wx import *
from wxPython.grid import wxGridPtr
#----------------------------------------------------------------------------
# event class and macors
wxEVT_COMMAND_GRID_COL_MOVE = wxNewEventType()
wxEVT_COMMAND_GRID_ROW_MOVE = wxNewEventType()
def EVT_GRID_COL_MOVE(win, id, func):
win.Connect(id, -1, wxEVT_COMMAND_GRID_COL_MOVE, func)
def EVT_GRID_ROW_MOVE(win,id,func):
win.Connect(id, -1, wxEVT_COMMAND_GRID_ROW_MOVE, func)
class wxGridColMoveEvent(wxPyCommandEvent):
def __init__(self, id, dCol, bCol):
wxPyCommandEvent.__init__(self, id = id)
self.SetEventType(wxEVT_COMMAND_GRID_COL_MOVE)
self.moveColumn = dCol
self.beforeColumn = bCol
def GetMoveColumn(self):
return self.moveColumn
def GetBeforeColumn(self):
return self.beforeColumn
class wxGridRowMoveEvent(wxPyCommandEvent):
def __init__(self, id, dRow, bRow):
wxPyCommandEvent.__init__(self,id = id)
self.SetEventType(wxEVT_COMMAND_GRID_ROW_MOVE)
self.moveRow = dRow
self.beforeRow = bRow
def GetMoveRow(self):
return self.moveRow
def GetBeforeRow(self):
return self.beforeRow
#----------------------------------------------------------------------------
# graft new methods into the wxGridPtr class
def _ColToRect(self,col):
if self.GetNumberRows() > 0:
rect = self.CellToRect(0,col)
else:
rect = wxRect()
rect.height = self.GetColLabelSize()
rect.width = self.GetColSize(col)
for cCol in range(0,col):
rect.x += self.GetColSize(cCol)
rect.y = self.GetGridColLabelWindow().GetPosition()[1]
return rect
wxGridPtr.ColToRect = _ColToRect
def _RowToRect(self,row):
if self.GetNumberCols() > 0:
rect = self.CellToRect(row,0)
else:
rect = wxRect()
rect.width = self.GetRowLabelSize()
rect.height = self.GetRowSize(row)
for cRow in range(0,row):
rect.y += self.GetRowSize(cRow)
rect.x = self.GetGridRowLabelWindow().GetPosition()[0]
return rect
wxGridPtr.RowToRect = _RowToRect
#----------------------------------------------------------------------------
class ColDragWindow(wxWindow):
def __init__(self,parent,image,dragCol):
wxWindow.__init__(self,parent,wxSIMPLE_BORDER)
self.image = image
self.SetSize((self.image.GetWidth(),self.image.GetHeight()))
self.ux = parent.GetScrollPixelsPerUnit()[0]
self.moveColumn = dragCol
EVT_PAINT(self,self.OnPaint)
def DisplayAt(self,pos,y):
x = self.GetPositionTuple()[0]
if x == pos:
self.Refresh() # Need to display insertion point
else:
self.MoveXY(pos,y)
def GetMoveColumn(self):
return self.moveColumn
def _GetInsertionInfo(self):
parent = self.GetParent()
sx = parent.GetViewStart()[0] * self.ux
sx -= parent._rlSize
x = self.GetPositionTuple()[0]
w = self.GetSizeTuple()[0]
sCol = parent.XToCol(x + sx)
eCol = parent.XToCol(x + w + sx)
iPos = xPos = xCol = 99999
centerPos = x + sx + (w / 2)
for col in range(sCol,eCol + 1):
cx = parent.ColToRect(col)[0]
if abs(cx - centerPos) < iPos:
iPos = abs(cx - centerPos)
xCol = col
xPos = cx
if xCol < 0 or xCol > parent.GetNumberCols():
xCol = parent.GetNumberCols()
return (xPos - sx - x,xCol)
def GetInsertionColumn(self):
return self._GetInsertionInfo()[1]
def GetInsertionPos(self):
return self._GetInsertionInfo()[0]
def OnPaint(self,evt):
dc = wxPaintDC(self)
w,h = self.GetSize()
dc.DrawBitmap(self.image,0,0)
dc.SetPen(wxPen(wxBLACK,1,wxSOLID))
dc.SetBrush(wxTRANSPARENT_BRUSH)
dc.DrawRectangle(0,0,w,h)
iPos = self.GetInsertionPos()
dc.DrawLine(iPos,h - 10,iPos,h)
class RowDragWindow(wxWindow):
def __init__(self,parent,image,dragRow):
wxWindow.__init__(self,parent,wxSIMPLE_BORDER)
self.image = image
self.SetSize((self.image.GetWidth(),self.image.GetHeight()))
self.uy = parent.GetScrollPixelsPerUnit()[1]
self.moveRow = dragRow
EVT_PAINT(self,self.OnPaint)
def DisplayAt(self,x,pos):
y = self.GetPositionTuple()[1]
if y == pos:
self.Refresh() # Need to display insertion point
else:
self.MoveXY(x,pos)
def GetMoveRow(self):
return self.moveRow
def _GetInsertionInfo(self):
parent = self.GetParent()
sy = parent.GetViewStart()[1] * self.uy
sy -= parent._clSize
y = self.GetPositionTuple()[1]
h = self.GetSizeTuple()[1]
sRow = parent.YToRow(y + sy)
eRow = parent.YToRow(y + h + sy)
iPos = yPos = yRow = 99999
centerPos = y + sy + (h / 2)
for row in range(sRow,eRow + 1):
cy = parent.RowToRect(row)[1]
if abs(cy - centerPos) < iPos:
iPos = abs(cy - centerPos)
yRow = row
yPos = cy
if yRow < 0 or yRow > parent.GetNumberRows():
yRow = parent.GetNumberRows()
return (yPos - sy - y,yRow)
def GetInsertionRow(self):
return self._GetInsertionInfo()[1]
def GetInsertionPos(self):
return self._GetInsertionInfo()[0]
def OnPaint(self,evt):
dc = wxPaintDC(self)
w,h = self.GetSize()
dc.DrawBitmap(self.image,0,0)
dc.SetPen(wxPen(wxBLACK,1,wxSOLID))
dc.SetBrush(wxTRANSPARENT_BRUSH)
dc.DrawRectangle(0,0,w,h)
iPos = self.GetInsertionPos()
dc.DrawLine(w - 10,iPos,w,iPos)
#----------------------------------------------------------------------------
class wxGridColMover(wxEvtHandler):
def __init__(self,grid):
wxEvtHandler.__init__(self)
self.grid = grid
self.grid._rlSize = self.grid.GetRowLabelSize()
self.lwin = grid.GetGridColLabelWindow()
self.lwin.PushEventHandler(self)
self.colWin = None
self.ux = self.grid.GetScrollPixelsPerUnit()[0]
self.startX = -10
self.cellX = 0
self.didMove = false
self.isDragging = false
EVT_MOTION(self,self.OnMouseMove)
EVT_LEFT_DOWN(self,self.OnPress)
EVT_LEFT_UP(self,self.OnRelease)
def OnMouseMove(self,evt):
if self.isDragging:
if abs(self.startX - evt.m_x) >= 3:
self.didMove = true
sx,y = self.grid.GetViewStart()
w,h = self.lwin.GetClientSizeTuple()
x = sx * self.ux
if (evt.m_x + x) < x:
x = evt.m_x + x
elif evt.m_x > w:
x += evt.m_x - w
if x < 1: x = 0
else: x /= self.ux
if x != sx:
if wxPlatform == '__WXMSW__':
self.colWin.Show(false)
self.grid.Scroll(x,y)
x,y = self.lwin.ClientToScreenXY(evt.m_x,0)
x,y = self.grid.ScreenToClientXY(x,y)
if not self.colWin.IsShown():
self.colWin.Show(true)
px = x - self.cellX
if px < 0 + self.grid._rlSize: px = 0 + self.grid._rlSize
if px > w - self.colWin.GetSizeTuple()[0] + self.grid._rlSize:
px = w - self.colWin.GetSizeTuple()[0] + self.grid._rlSize
self.colWin.DisplayAt(px,y)
return
evt.Skip()
def OnPress(self,evt):
self.startX = evt.m_x
sx = self.grid.GetViewStart()[0] * self.ux
sx -= self.grid._rlSize
px,py = self.lwin.ClientToScreenXY(evt.m_x,evt.m_y)
px,py = self.grid.ScreenToClientXY(px,py)
if self.grid.XToEdgeOfCol(px + sx) != wxNOT_FOUND:
evt.Skip()
return
self.isDragging = true
self.didMove = false
col = self.grid.XToCol(px + sx)
rect = self.grid.ColToRect(col)
self.cellX = px + sx - rect.x
size = self.lwin.GetSizeTuple()
rect.y = 0
rect.x -= sx + self.grid._rlSize
rect.height = size[1]
colImg = self._CaptureImage(rect)
self.colWin = ColDragWindow(self.grid,colImg,col)
self.colWin.Show(false)
self.lwin.CaptureMouse()
def OnRelease(self,evt):
if self.isDragging:
self.lwin.ReleaseMouse()
self.colWin.Show(false)
self.isDragging = false
if not self.didMove:
px = self.lwin.ClientToScreenXY(self.startX,0)[0]
px = self.grid.ScreenToClientXY(px,0)[0]
sx = self.grid.GetViewStart()[0] * self.ux
sx -= self.grid._rlSize
col = self.grid.XToCol(px+sx)
if col != wxNOT_FOUND:
self.grid.SelectCol(col,evt.m_controlDown)
return
else:
bCol = self.colWin.GetInsertionColumn()
dCol = self.colWin.GetMoveColumn()
wxPostEvent(self,wxGridColMoveEvent(self.grid.GetId(),
dCol,bCol))
self.colWin.Destroy()
evt.Skip()
def _CaptureImage(self,rect):
bmp = wxEmptyBitmap(rect.width,rect.height)
memdc = wxMemoryDC()
memdc.SelectObject(bmp)
dc = wxWindowDC(self.lwin)
memdc.Blit(0,0,rect.width,rect.height,dc,rect.x,rect.y)
memdc.SelectObject(wxNullBitmap)
return bmp
class wxGridRowMover(wxEvtHandler):
def __init__(self,grid):
wxEvtHandler.__init__(self)
self.grid = grid
self.grid._clSize = self.grid.GetColLabelSize()
self.lwin = grid.GetGridRowLabelWindow()
self.lwin.PushEventHandler(self)
self.rowWin = None
self.uy = self.grid.GetScrollPixelsPerUnit()[1]
self.startY = -10
self.cellY = 0
self.didMove = false
self.isDragging = false
EVT_MOTION(self,self.OnMouseMove)
EVT_LEFT_DOWN(self,self.OnPress)
EVT_LEFT_UP(self,self.OnRelease)
def OnMouseMove(self,evt):
if self.isDragging:
if abs(self.startY - evt.m_y) >= 3:
self.didMove = true
x,sy = self.grid.GetViewStart()
w,h = self.lwin.GetClientSizeTuple()
y = sy * self.uy
if (evt.m_y + y) < y:
y = evt.m_y + y
elif evt.m_y > h:
y += evt.m_y - h
if y < 1: y = 0
else: y /= self.uy
if y != sy:
if wxPlatform == '__WXMSW__':
self.rowWin.Show(false)
self.grid.Scroll(x,y)
x,y = self.lwin.ClientToScreenXY(0,evt.m_y)
x,y = self.grid.ScreenToClientXY(x,y)
if not self.rowWin.IsShown():
self.rowWin.Show(true)
py = y - self.cellY
if py < 0 + self.grid._clSize: py = 0 + self.grid._clSize
if py > h - self.rowWin.GetSizeTuple()[1] + self.grid._clSize:
py = h - self.rowWin.GetSizeTuple()[1] + self.grid._clSize
self.rowWin.DisplayAt(x,py)
return
evt.Skip()
def OnPress(self,evt):
self.startY = evt.m_y
sy = self.grid.GetViewStart()[1] * self.uy
sy -= self.grid._clSize
px,py = self.lwin.ClientToScreenXY(evt.m_x,evt.m_y)
px,py = self.grid.ScreenToClientXY(px,py)
if self.grid.YToEdgeOfRow(py + sy) != wxNOT_FOUND:
evt.Skip()
return
self.isDragging = true
self.didMove = false
row = self.grid.YToRow(py + sy)
rect = self.grid.RowToRect(row)
self.cellY = py + sy - rect.y
size = self.lwin.GetSizeTuple()
rect.x = 0
rect.y -= sy + self.grid._clSize
rect.width = size[0]
rowImg = self._CaptureImage(rect)
self.rowWin = RowDragWindow(self.grid,rowImg,row)
self.rowWin.Show(false)
self.lwin.CaptureMouse()
def OnRelease(self,evt):
if self.isDragging:
self.lwin.ReleaseMouse()
self.rowWin.Show(false)
self.isDragging = false
if not self.didMove:
py = self.lwin.ClientToScreenXY(0,self.startY)[1]
py = self.grid.ScreenToClientXY(0,py)[1]
sy = self.grid.GetViewStart()[1] * self.uy
sy -= self.grid._clSize
row = self.grid.YToRow(py + sy)
if row != wxNOT_FOUND:
self.grid.SelectRow(row,evt.m_controlDown)
return
else:
bRow = self.rowWin.GetInsertionRow()
dRow = self.rowWin.GetMoveRow()
wxPostEvent(self,wxGridRowMoveEvent(self.grid.GetId(),
dRow,bRow))
self.rowWin.Destroy()
evt.Skip()
def _CaptureImage(self,rect):
bmp = wxEmptyBitmap(rect.width,rect.height)
memdc = wxMemoryDC()
memdc.SelectObject(bmp)
dc = wxWindowDC(self.lwin)
memdc.Blit(0,0,rect.width,rect.height,dc,rect.x,rect.y)
memdc.SelectObject(wxNullBitmap)
return bmp
#----------------------------------------------------------------------------