wxWidgets/wxPython/demo/CustomDragAndDrop.py

349 lines
10 KiB
Python
Raw Normal View History

import cPickle
import wx
#----------------------------------------------------------------------
class DoodlePad(wx.Window):
def __init__(self, parent, log):
wx.Window.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
self.log = log
self.SetBackgroundColour(wx.WHITE)
self.lines = []
self.x = self.y = 0
self.SetMode("Draw")
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)
self.Bind(wx.EVT_MOTION, self.OnMotion)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def SetMode(self, mode):
self.mode = mode
if self.mode == "Draw":
self.SetCursor(wx.StockCursor(wx.CURSOR_PENCIL))
else:
self.SetCursor(wx.STANDARD_CURSOR)
def OnPaint(self, event):
dc = wx.PaintDC(self)
self.DrawSavedLines(dc)
def DrawSavedLines(self, dc):
dc.BeginDrawing()
dc.SetPen(wx.Pen(wx.BLUE, 3))
for line in self.lines:
for coords in line:
dc.DrawLine(*coords)
dc.EndDrawing()
def OnLeftDown(self, event):
if self.mode == "Drag":
self.StartDragOpperation()
elif self.mode == "Draw":
self.curLine = []
self.x, self.y = event.GetPositionTuple()
self.CaptureMouse()
else:
wx.Bell()
self.log.write("unknown mode!\n")
def OnLeftUp(self, event):
if self.HasCapture():
self.lines.append(self.curLine)
self.curLine = []
self.ReleaseMouse()
def OnRightUp(self, event):
self.lines = []
self.Refresh()
def OnMotion(self, event):
if self.HasCapture() and event.Dragging() and not self.mode == "Drag":
dc = wx.ClientDC(self)
dc.BeginDrawing()
dc.SetPen(wx.Pen(wx.BLUE, 3))
coords = (self.x, self.y) + event.GetPositionTuple()
self.curLine.append(coords)
dc.DrawLine(*coords)
self.x, self.y = event.GetPositionTuple()
dc.EndDrawing()
def StartDragOpperation(self):
# pickle the lines list
linesdata = cPickle.dumps(self.lines, 1)
# create our own data format and use it in a
# custom data object
ldata = wx.CustomDataObject("DoodleLines")
ldata.SetData(linesdata)
# Also create a Bitmap version of the drawing
size = self.GetSize()
bmp = wx.EmptyBitmap(size.width, size.height)
dc = wx.MemoryDC()
dc.SelectObject(bmp)
dc.SetBackground(wx.WHITE_BRUSH)
dc.Clear()
self.DrawSavedLines(dc)
dc.SelectObject(wx.NullBitmap)
# Now make a data object for the bitmap and also a composite
# data object holding both of the others.
bdata = wx.BitmapDataObject(bmp)
data = wx.DataObjectComposite()
data.Add(ldata)
data.Add(bdata)
# And finally, create the drop source and begin the drag
# and drop opperation
dropSource = wx.DropSource(self)
dropSource.SetData(data)
self.log.WriteText("Begining DragDrop\n")
result = dropSource.DoDragDrop(wx.Drag_AllowMove)
self.log.WriteText("DragDrop completed: %d\n" % result)
if result == wx.DragMove:
self.lines = []
self.Refresh()
#----------------------------------------------------------------------
class DoodleDropTarget(wx.PyDropTarget):
def __init__(self, window, log):
wx.PyDropTarget.__init__(self)
self.log = log
self.dv = window
# specify the type of data we will accept
self.data = wx.CustomDataObject("DoodleLines")
self.SetDataObject(self.data)
# some virtual methods that track the progress of the drag
def OnEnter(self, x, y, d):
self.log.WriteText("OnEnter: %d, %d, %d\n" % (x, y, d))
return d
def OnLeave(self):
self.log.WriteText("OnLeave\n")
def OnDrop(self, x, y):
self.log.WriteText("OnDrop: %d %d\n" % (x, y))
return True
def OnDragOver(self, x, y, d):
#self.log.WriteText("OnDragOver: %d, %d, %d\n" % (x, y, d))
# The value returned here tells the source what kind of visual
# feedback to give. For example, if wxDragCopy is returned then
# only the copy cursor will be shown, even if the source allows
# moves. You can use the passed in (x,y) to determine what kind
# of feedback to give. In this case we return the suggested value
# which is based on whether the Ctrl key is pressed.
return d
# Called when OnDrop returns True. We need to get the data and
# do something with it.
def OnData(self, x, y, d):
self.log.WriteText("OnData: %d, %d, %d\n" % (x, y, d))
# copy the data from the drag source to our data object
if self.GetData():
# convert it back to a list of lines and give it to the viewer
linesdata = self.data.GetData()
lines = cPickle.loads(linesdata)
self.dv.SetLines(lines)
# what is returned signals the source what to do
# with the original data (move, copy, etc.) In this
# case we just return the suggested value given to us.
return d
class DoodleViewer(wx.Window):
def __init__(self, parent, log):
wx.Window.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
self.log = log
self.SetBackgroundColour(wx.WHITE)
self.lines = []
self.x = self.y = 0
dt = DoodleDropTarget(self, log)
self.SetDropTarget(dt)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def SetLines(self, lines):
self.lines = lines
self.Refresh()
def OnPaint(self, event):
dc = wx.PaintDC(self)
self.DrawSavedLines(dc)
def DrawSavedLines(self, dc):
dc.BeginDrawing()
dc.SetPen(wx.Pen(wx.RED, 3))
for line in self.lines:
for coords in line:
dc.DrawLine(*coords)
dc.EndDrawing()
#----------------------------------------------------------------------
class CustomDnDPanel(wx.Panel):
def __init__(self, parent, log):
wx.Panel.__init__(self, parent, -1)
self.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, False))
# Make the controls
text1 = wx.StaticText(self, -1,
"Draw a little picture in this window\n"
"then switch the mode below and drag the\n"
"picture to the lower window or to another\n"
"application that accepts Bitmaps as a\n"
"drop target.\n"
)
rb1 = wx.RadioButton(self, -1, "Draw", style=wx.RB_GROUP)
rb1.SetValue(True)
rb2 = wx.RadioButton(self, -1, "Drag")
rb2.SetValue(False)
text2 = wx.StaticText(self, -1,
"The lower window is accepting a\n"
"custom data type that is a pickled\n"
"Python list of lines data.")
self.pad = DoodlePad(self, log)
view = DoodleViewer(self, log)
# put them in sizers
sizer = wx.BoxSizer(wx.HORIZONTAL)
box = wx.BoxSizer(wx.VERTICAL)
rbox = wx.BoxSizer(wx.HORIZONTAL)
rbox.Add(rb1)
rbox.Add(rb2)
box.Add(text1, 0, wx.ALL, 10)
box.Add(rbox, 0, wx.ALIGN_CENTER)
box.Add((10,90))
box.Add(text2, 0, wx.ALL, 10)
sizer.Add(box)
dndsizer = wx.BoxSizer(wx.VERTICAL)
dndsizer.Add(self.pad, 1, wx.EXPAND|wx.ALL, 5)
dndsizer.Add(view, 1, wx.EXPAND|wx.ALL, 5)
sizer.Add(dndsizer, 1, wx.EXPAND)
self.SetAutoLayout(True)
self.SetSizer(sizer)
# Events
self.Bind(wx.EVT_RADIOBUTTON, self.OnRadioButton, rb1)
self.Bind(wx.EVT_RADIOBUTTON, self.OnRadioButton, rb2)
def OnRadioButton(self, evt):
rb = self.FindWindowById(evt.GetId())
self.pad.SetMode(rb.GetLabel())
#----------------------------------------------------------------------
#----------------------------------------------------------------------
class TestPanel(wx.Panel):
def __init__(self, parent, log):
wx.Panel.__init__(self, parent, -1)
self.SetAutoLayout(True)
sizer = wx.BoxSizer(wx.VERTICAL)
msg = "Custom Drag-And-Drop"
text = wx.StaticText(self, -1, "", style=wx.ALIGN_CENTRE)
text.SetFont(wx.Font(24, wx.SWISS, wx.NORMAL, wx.BOLD, False))
text.SetLabel(msg)
w,h = text.GetTextExtent(msg)
text.SetSize(wx.Size(w,h+1))
text.SetForegroundColour(wx.BLUE)
sizer.Add(text, 0, wx.EXPAND|wx.ALL, 5)
sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND)
sizer.Add(CustomDnDPanel(self, log), 1, wx.EXPAND)
self.SetSizer(sizer)
#----------------------------------------------------------------------
def runTest(frame, nb, log):
#win = TestPanel(nb, log)
win = CustomDnDPanel(nb, log)
return win
if __name__ == '__main__':
import sys
class DummyLog:
def WriteText(self, text):
sys.stdout.write(text)
class TestApp(wx.App):
def OnInit(self):
wx.InitAllImageHandlers()
self.MakeFrame()
return True
def MakeFrame(self, event=None):
frame = wx.Frame(None, -1, "Custom Drag and Drop", size=(550,400))
menu = wx.Menu()
item = menu.Append(-1, "Window")
mb = wx.MenuBar()
mb.Append(menu, "New")
frame.SetMenuBar(mb)
frame.Bind(wx.EVT_MENU, self.MakeFrame, item)
panel = TestPanel(frame, DummyLog())
frame.Show(True)
self.SetTopWindow(frame)
#----------------------------------------------------------------------
app = TestApp(0)
app.MainLoop()
#----------------------------------------------------------------------
overview = """<html><body>
This demo shows Drag and Drop using a custom data type and a custom
data object. A type called "DoodleLines" is created and a Python
Pickle of a list is actually transfered in the drag and drop
opperation.
A second data object is also created containing a bitmap of the image
and is made available to any drop target that accepts bitmaps, such as
MS Word.
The two data objects are combined in a wx.DataObjectComposite and the
rest is handled by the framework.
</body></html>
"""