This code successfully draws the SVG onto the screen. However, when I resize the window, the original image stays superimposed over the screen, while it redraws it underneath.
Before Resizing:
After Maximizing:
It's especially noticable if you drag to resize
# import igraph # either uncomment and install igraph or provide output.svg in the same dir
import wx
import wx.svg
class NNGui(wx.Frame):
def __init__(self, parent, title):
super().__init__(parent, title=title, size=(800, 600))
self.panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
# main graphics box
self.screen = MainScreen(self.panel)
vbox.Add(self.screen, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
self.Bind(wx.EVT_SIZE, self.on_resize)
self.Bind(wx.EVT_MAXIMIZE, self.on_resize)
# command box
self.cmd_box = wx.TextCtrl(self.panel, style=wx.TE_PROCESS_ENTER | wx.TE_PROCESS_TAB)
vbox.Add(self.cmd_box, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=10)
self.cmd_box.Bind(wx.EVT_CHAR, self.do_char)
self.panel.SetSizer(vbox)
self.Layout()
self.Centre()
def do_char(self, e):
# handle keypresses
e.Skip()
def on_resize(self, e):
print('resize!')
# self.screen = MainScreen(self.panel)
self.screen.Refresh() # thank you Rolf-of-Saxony
# self.panel.Refresh()
# self.Refresh()
self.screen.Update()
# self.panel.Update()
# self.Update()
e.Skip()
class MainScreen(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
self.img = wx.svg.SVGimage.CreateFromFile('output.svg')
self.Bind(wx.EVT_PAINT, self.on_paint)
def on_paint(self, e):
print('screen painted!')
dc = wx.PaintDC(self)
dc.SetBackground(wx.Brush('black'))
dc.Clear()
dc_dim = min(self.Size.width, self.Size.height)
img_dim = min(self.img.width, self.img.height)
scale = dc_dim / img_dim
width = int(self.img.width * scale)
height = int(self.img.height * scale)
# ctx = wx.GraphicsContext.Create(dc)
# self.img.RenderToGC(ctx, scale)
bmp = self.img.ConvertToBitmap(scale=scale, width=width, height=height)
px_to_center = int((self.Size.width - width) / 2)
dc.DrawBitmap(bmp, px_to_center, 0)
e.Skip()
class NNGraph:
def __init__(self):
self.g = igraph.Graph.GRG(50, 0.2)
def write_svg(self, filename='output.svg'):
assert filename.endswith('.svg')
igraph.plot(self.g, filename)
def main():
# graph = NNGraph() # either uncomment and install igraph or provide output.svg in the same dir
# graph.write_svg()
app = wx.App()
frame = NNGui(None, title='NeuronicNodes')
frame.Show()
app.MainLoop()
if __name__ == '__main__':
main()
I've tried applying Update() to several panels, bound events for EVT_SIZE, EVT_PAINT, tried recreating the panel... I'm not sure what I'm missing.
This code reads a picture and put it as background in a window.
There are two issues I cannot explain:
once you import the picture, clicking the red "X" in the top-right corner doesn't close the window.
if you try to drag the image, the program crashes.
Why is it so?
Thank you
import wx
import wx.lib.buttons as buttons
class Main(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, id=-1, title=title, size=(300, 300))
self.initUI()
self.panel = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, size=(10000, 10000))
self.backGroundImage=''
self.Layout()
def initUI(self):
menubar = wx.MenuBar()
fileMenu = wx.Menu()
fileMenu.AppendSeparator()
imp = wx.Menu()
importBackgroundButton = imp.Append(wx.ID_ANY, 'Import background')
self.Bind(wx.EVT_MENU, self.OnImportBackground, importBackgroundButton)
fileMenu.AppendMenu(wx.ID_ANY, 'I&mport', imp)
menubar.Append(fileMenu, '&File')
self.SetMenuBar(menubar)
self.SetTitle('test')
self.Centre()
self.Show(True)
#load background
def OnImportBackground(self, e):
app = wx.App(None)
style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
dialog = wx.FileDialog(None, 'Open', wildcard='*.png', style=style)
if dialog.ShowModal() == wx.ID_OK:
path = dialog.GetPath()
else:
path = None
dialog.Destroy()
self.backgroundImage = ButtonImage(self, self.panel, path, (0, 0))
W = self.backgroundImage.bmp.GetSize()[0]
H = self.backgroundImage.bmp.GetSize()[1]
self.SetSize((W+16, H+58))
self.Refresh()
#crash
class ButtonImage():
def __init__(self, parent, panel, nameImage, pos):
self.panel = panel
self.bmp = wx.Bitmap(nameImage, wx.BITMAP_TYPE_ANY)
self.maxPiecePositionX = self.panel.GetSize()[0] - self.bmp.GetSize()[0]
self.maxPiecePositionY = self.panel.GetSize()[1] - self.bmp.GetSize()[1]
self.bmapBtn = wx.BitmapButton(self.panel, id=wx.ID_ANY, bitmap=self.bmp, style=wx.NO_BORDER, pos=pos)
self.bmapBtn.Bind(wx.EVT_LEFT_DOWN, self.OnClickDown, self.bmapBtn)
self.bmapBtn.Bind(wx.EVT_LEFT_UP, self.OnClickUp, self.bmapBtn)
self.bmapBtn.Bind(wx.EVT_MOTION, self.MoveButton, self.bmapBtn)
self.hold = 0
self.holdPosition = (0, 0)
def EnterButton(self, event):
pass
def LeaveButton(self, event):
self.hold = 0
def OnClickDown(self, event):
obj = event.GetEventObject()
self.hold = 1
self.holdPosition = (event.GetX(), event.GetY())
def OnClickUp(self, event):
self.hold = 0
def MoveButton(self, event):
deltaX, deltaY = 0, 0
if self.hold:
deltaX = event.GetPosition()[0] - self.holdPosition[0]
deltaY = event.GetPosition()[1] - self.holdPosition[1]
newPositionX = self.bmapBtn.GetPosition()[0] + deltaX
newPositionY = self.bmapBtn.GetPosition()[1] + deltaY
if (0 < newPositionX < self.maxPiecePositionX) and (0 < newPositionY < self.maxPiecePositionY):
self.bmapBtn.SetPosition((newPositionX, newPositionY))
else:
self.holdPosition = self.holdPosition[0] + deltaX, self.holdPosition[1] + deltaY
self.bmapBtn.Raise()
self.bmapBtn.Refresh()
app = wx.App()
frame = Main(None, "Test")
frame.Show()
app.MainLoop()
This example has so many issues that it would take a long time to explain it. E. g., you create a new ButtonImage, which is essentially a wx.BitmapButton, every time you call OnImportBackground without destroying the old one, stacking up a collection of bitmap buttons without properly layouting them.
But what is driving the nail into the coffin that you instantiate a new wx.App every time OnImportBackground is called. If you remove this line (which is completely pointless), the frame can at least be closed.
But to see for "the right way (TM)" to do it, look at this stackoverflow post.
I am working with python v2.7 and wxPython v3.0 on Windows 8 OS.
The code provided below simply creates a transparent panel named as myPanel that contains a button. The transparent panel is created on a mainPanel which contains an image as a background.
The transparent panel can be dragged around in the frame.
Problem: After dragging the transparent panel I observed that the background of the transparent panel is not updated automatically. How to update it automatically? How ever if I minimize the gui window and restore it again, the background of the transparent panel is updated automatically! I don't understand the reason of this affect?
I tried using Refresh(), Update() etc. in MouseUp(self, e) method, but unfortunately nothing helped.
Here are the screenshots of the app. The initial state is shown in the image below when the app starts:
After dragging the transparent panel, the background is not updated as shown in the image below:
After minimizing the app window and then restoring it, you'll notice that the background of the transparent panel is updated automatically as shown in the image below:
Code: The image used in the code can be downloaded from here. globe.jpg
import wx
class gui(wx.Frame):
def __init__(self, parent, id, title):
self.d = d = {}
wx.Frame.__init__(self, None, id, title, size=(260,260), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER)
statusbar = self.CreateStatusBar()
self.mainPanel = mainPanel = wx.Panel(self)
self.mainSizer = mainSizer = wx.BoxSizer(wx.VERTICAL)
self.myPanel = myPanel = wx.Panel(mainPanel, -1, style=wx.TRANSPARENT_WINDOW, size=(80,80))
button1 = wx.Button(myPanel, -1, size=(30,30), pos=(10,10))
button1.SetBackgroundColour('#fff111')
mainSizer.Add(myPanel, 0, wx.ALL, 0)
myPanel.Bind(wx.EVT_LEFT_DOWN, self.MouseDown)
myPanel.Bind(wx.EVT_MOTION, self.MouseMove)
myPanel.Bind(wx.EVT_LEFT_UP, self.MouseUp)
image_file = 'globe.jpg'
bmp1 = wx.Image(image_file, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
wx.StaticBitmap(mainPanel, -1, bmp1, (0, 0))
mainPanel.Bind(wx.EVT_MOTION, self.MouseMove)
mainPanel.Bind(wx.EVT_LEFT_UP, self.MouseUp)
mainPanel.SetSizer(mainSizer)
mainPanel.Layout()
def MouseDown(self, e):
o = e.GetEventObject()
sx,sy = self.mainPanel.ScreenToClient(o.GetPositionTuple())
dx,dy = self.mainPanel.ScreenToClient(wx.GetMousePosition())
o._x,o._y = (sx-dx, sy-dy)
self.d['d'] = o
def MouseMove(self, e):
try:
if 'd' in self.d:
o = self.d['d']
x, y = wx.GetMousePosition()
o.SetPosition(wx.Point(x+o._x,y+o._y))
except: pass
def MouseUp(self, e):
try:
if 'd' in self.d: del self.d['d']
except: pass
if __name__=='__main__':
app = wx.App()
frame = gui(parent=None, id=-1, title="Test")
frame.Show()
app.MainLoop()
Thank you for your time!
You can create a custom panel and then draw a portion of the globe on that panel based on where it's located on top of the parent frame. This method "fakes" the transparency. I've included an example below.
import wx
class CustomPanel(wx.Panel):
def __init__(self,parent):
wx.Panel.__init__(self,parent,-1,size=(80,80))
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, evt):
parentw,parenth = self.GetParent().GetSize()
image = wx.Image('globe.jpg', wx.BITMAP_TYPE_ANY)
x,y = self.GetPosition()
mywidth,myheight = self.GetSize()
if x + mywidth >= parentw:
mywidth = parentw - x
if y + myheight >= parenth:
myheight = parenth - y
drawx = 0
drawy = 0
if x < 0:
drawx = abs(x)
x = 0
if y < 0:
drawy = abs(y)
y = 0
r = wx.Rect(x,y,mywidth,myheight)
try:
image = image.GetSubImage(r)
except:
# rectangle is out of parent
print 'rect ',r ,' is out of parent frame'
return
bitmap = image.ConvertToBitmap()
pdc = wx.PaintDC(self)
pdc.DrawBitmap(bitmap, drawx, drawy)
class gui(wx.Frame):
def __init__(self, parent, id, title):
self.d = d = {}
wx.Frame.__init__(self, None, id, title, size=(260,260), style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER | wx.CLIP_CHILDREN)
statusbar = self.CreateStatusBar()
self.mainPanel = mainPanel = wx.Panel(self)
self.mainSizer = mainSizer = wx.BoxSizer(wx.VERTICAL)
#self.myPanel = myPanel = wx.Panel(mainPanel, -1, style=wx.TRANSPARENT_WINDOW, size=(80,80))
self.myPanel = myPanel = CustomPanel(mainPanel)
button1 = wx.Button(myPanel, -1, size=(30,30), pos=(10,10))
button1.SetBackgroundColour('#fff111')
button2 = wx.Button(myPanel, -1, size=(30,30), pos=(40,40))
button2.SetBackgroundColour('#fff111')
mainSizer.Add(myPanel, 0, wx.ALL, 0)
myPanel.Bind(wx.EVT_LEFT_DOWN, self.MouseDown)
myPanel.Bind(wx.EVT_MOTION, self.MouseMove)
myPanel.Bind(wx.EVT_LEFT_UP, self.MouseUp)
image_file = 'globe.jpg'
bmp1 = wx.Image(image_file, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
wx.StaticBitmap(mainPanel, -1, bmp1, (0, 0))
mainPanel.Bind(wx.EVT_MOTION, self.MouseMove)
mainPanel.Bind(wx.EVT_LEFT_UP, self.MouseUp)
mainPanel.SetSizer(mainSizer)
mainPanel.Layout()
def MouseDown(self, e):
o = e.GetEventObject()
sx,sy = self.mainPanel.ScreenToClient(o.GetPositionTuple())
dx,dy = self.mainPanel.ScreenToClient(wx.GetMousePosition())
o._x,o._y = (sx-dx, sy-dy)
self.d['d'] = o
def MouseMove(self, e):
try:
if 'd' in self.d:
o = self.d['d']
x, y = wx.GetMousePosition()
o.SetPosition(wx.Point(x+o._x,y+o._y))
self.myPanel.Refresh()
except: pass
def MouseUp(self, e):
try:
if 'd' in self.d: del self.d['d']
except: pass
if __name__=='__main__':
app = wx.App()
frame = gui(parent=None, id=-1, title="Test")
frame.Show()
app.MainLoop()
I want to put such an image in a wx.Panel :
The animals should be "buttons" so that if I click on them, their image changes, and if I reclick, the image returns to normal (thus the animals can be considered as simple BitmapToggleButtons, as suggested by another question here on SO)
This panel should be resized/rescaled (all all the children images / togglebuttons too!) keeping the aspect ratio, if the parent wx.Panel is resized to something smaller for example (like would do the standard Windows Photo Viewer : http://res1.windows.microsoft.com/resbox/en/windows%207/main/7eaf462a-86dd-42d2-a789-7413f5472dae_63.jpg)
I am still a bit lost on : how to implement such a clickable (with toggle buttons) and rescalable Canvas?
Edit : I started with something fruitful here Rescale image when parent is resized in wxPython, but now I'm totally stuck about how to continue (detect clicks, update buttons with direct DC Painting ?), that's why the bounty.
You will have to implement your own hit testing, i.e. be able to determine where each animal is -- this is the difficult part and there is really nothing in wxWidgets to help you with this. The rest is relatively simple, you might even be able to use the existing wxMouseEventsManager to avoid writing the boilerplate code yourself (but if you can't, you can at least look at its implementation, which is done entirely in wxWidgets itself, to see what you need to do).
Depending on how much of this is already written, you may want to take a look at FloatCanvas (it's in the wxPython library).
If you have most of the code-base done, you can use a hit test, which is rather simple. Just make a dictionary with the [x][y] coordinates as keys which has the the BitmapTogglebutton as its value.
Here's some code that does something similar (it's been a while since I've used wxPython so it may not be 100%):
def onLeftDown( event ):
x,y = event.GetX(), event.GetY()
hitmap_x = hitmap.get(x,None)
if hitmap_x:
btn = hitmap_x.get(y, None)
...stuff with btn like toggles
I made some code for practice recently. It may somehow match with your requirement.
Code is ugly and mess because I'm a newcomer to python.
Support:
image on the backgroud draggable
image animating with double-click
background and image on it resizeable
Notice:
you need have pygame to run the code
you can load real image by replacing PyGamePseudoImage()
image coordinate adjustment is not smooth enough while zoom in/out
Code:
import wx
import pygame
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
BLUE = ( 0, 0, 255)
GREEN = ( 0, 255, 0)
RED = (255, 0, 0)
pygame.font.init()
try:
regular_font_file = os.path.join(os.path.dirname(__file__), "Vera.ttf")
bold_font_file = os.path.join(os.path.dirname(__file__), "VeraBd.ttf")
# Check for cx_Freeze
#
if "frozen" in sys.__dict__.keys() and sys.frozen:
regular_font_file = os.path.join(sys.path[1], "Vera.ttf")
bold_font_file = os.path.join(sys.path[1], "VeraBd.ttf")
BIG_FONT = pygame.font.Font(regular_font_file, 30)
SMALL_FONT = pygame.font.Font(regular_font_file, 12)
BOLD_FONT = pygame.font.Font(bold_font_file, 12)
except:
# TODO: log used font: pygame.font.get_default_font()
#print("Could not load {0}".format(os.path.join(os.path.dirname(__file__), "Vera.ttf")))
BIG_FONT = pygame.font.Font(None, 40)
SMALL_FONT = BOLD_FONT = pygame.font.Font(None, 20)
class PyGamePseudoImage():
def __init__(self, size, color):
self.screen = pygame.Surface(size, 0, 32)
self.screen.fill(color)
def getImage(self):
return self.screen
class __MouseMixin:
def onLeftUp(self, event):
pass
def onLeftDown(self, event):
pass
def onLeftDClick(self, event):
pass
def onRightUp(self, event):
pass
def onRightDown(self, event):
pass
def onDragging(self, event):
pass
def onMouseEnter(self, event):
pass
def OnMouseHandler(self, event):
event.Skip()
if event.LeftUp():
self.onLeftUp(event)
elif event.LeftDown():
self.onLeftDown(event)
elif event.LeftDClick():
self.onLeftDClick(event)
elif event.RightUp():
self.onRightUp(event)
elif event.RightDown():
self.onRightDown(event)
elif event.Dragging() and event.LeftIsDown():
self.onDragging(event)
pass
class DragSprite(__MouseMixin, pygame.sprite.Sprite):
SPRITE_BUTTON, SPRITE_TRANSPORTER = range(2)
def __init__(self, parent=None):
pygame.sprite.Sprite.__init__(self)
self.is_select = 0
self.lastPos = 0
self.lastUpdate = 0
self.parent = parent
def setLastPos(self, pos):
self.lastPos = pos
def move(self, pos):
dx = pos[0] - self.lastPos[0]
dy = pos[1] - self.lastPos[1]
self.lastPos = pos
center = (self.rect.center[0] + dx, self.rect.center[1] + dy)
self.rect.center = center
return
def isSelected(self):
return self.is_select
def setSelect(self, is_select):
self.is_select = is_select
return
def update(self, current_time):
return
def drawBoader(image, rect):
W,H = (rect.width, rect.height)
yellow = (255, 255, 0)
pygame.draw.rect(image, yellow, (0,0,W-2,H-2), 2)
class ButtonSprite(DragSprite):
def __init__(self, parent=None, initPos=(0,0), width=50, height=50, dicts=None):
DragSprite.__init__(self, parent)
self.type = DragSprite.SPRITE_BUTTON
self.resourceCfgDict = dicts
self.imageResource = {}
self.status = 0
self.index = 0
self.parent = parent
self.initPos = (initPos[0], initPos[1])
self.width = width
self.height = height
self.rectOnLoad = pygame.Rect(initPos, (width, height))
self.rect = self.rectOnLoad.copy()
self.operationOn = None
self.operationOff = None
self.operationDic = {"on": self.getOperationOnItem, "off": self.getOperationOffItem}
self.guiCfg = None
for dic in dicts:
self.loadImgResource(dic)
self.setCurrentResource("off")
def getOperationOnItem(self):
return self.operationOn
def getOperationOffItem(self):
return self.operationOff
def loadImgResource(self, dict):
"""
load image with pygame lib
"""
key = dict[0]
file_name = dict[1]
#image_file = pygame.image.load(file_name) #use this to load real image
image_file = PyGamePseudoImage((500,500), file_name).getImage()
imagedata = pygame.image.tostring(image_file, "RGBA")
imagesize = image_file.get_size()
imageSurface = pygame.image.fromstring(imagedata, imagesize , "RGBA")
self.imageResource[key] = (file_name, imageSurface)
def resizeResource(self, src, size):
return pygame.transform.smoothscale(src, size)
def setCurrentResource(self, status):
self.currentStatus = status
self.imageOnLoad = self.resizeResource(self.imageResource[status][1], (self.width, self.height))
self.image = pygame.transform.scale(self.imageOnLoad, (self.rect.width, self.rect.height))
def switchResource(self, index):
self.setCurrentResource(index)
def onZoomUpdate(self, zoomRatio):
parentRect = pygame.Rect(self.parent.GetRect())
dx = self.rectOnLoad.centerx - parentRect.centerx
dy = self.rectOnLoad.centery - parentRect.centery
self.rect.centerx = parentRect.centerx + dx*zoomRatio
self.rect.centery = parentRect.centery + dy*zoomRatio
self.rect.height = self.imageOnLoad.get_rect().height * zoomRatio
self.rect.width = self.imageOnLoad.get_rect().width * zoomRatio
self.image = pygame.transform.scale(self.imageOnLoad, (self.rect.width, self.rect.height))
def update(self, current_time, ratio):
if self.isSelected():
drawBoader(self.image, self.image.get_rect())
else:
pass
#self.image = self.imageOnLoad.copy()
def onRightUp(self, event):
print "onRightUp"
event.Skip(False)
pass
def onLeftDClick(self, event):
if self.currentStatus == "on":
self.setCurrentResource("off")
elif self.currentStatus == "off":
self.setCurrentResource("on")
return
def move(self, pos):
DragSprite.move(self, pos)
parentRect = pygame.Rect(self.parent.GetRect())
centerDx = self.rect.centerx - parentRect.centerx
centerDy = self.rect.centery - parentRect.centery
self.rectOnLoad.centerx = parentRect.centerx + centerDx/self.parent.zoomRatio
self.rectOnLoad.centery = parentRect.centery + centerDy/self.parent.zoomRatio
class MyHmiPanel(wx.Panel):
def __init__(self, parent, ID):
wx.Window.__init__(self, parent, ID)
self.parent = parent
self.hwnd = self.GetHandle()
self.size = self.GetSizeTuple()
self.size_dirty = True
self.rootSpriteGroup = pygame.sprite.LayeredUpdates()
self.timer = wx.Timer(self)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_TIMER, self.Update, self.timer)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.fps = 60.0
self.timespacing = 1000.0 / self.fps
self.timer.Start(self.timespacing, False)
self.previous_time = 0
self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
self.selectedSprite = None
self.zoomRatio = 1
self.background = None
self.bgRect = None
self.backgroundOnUpdate = None
self.bgRetOnUpdate = None
self.loadBackground()
self.addTestSprite()
def loadBackground(self):
#self.background = pygame.image.load(image_file) #use this to load real image
self.background = PyGamePseudoImage((500,500), (255, 0, 0)).getImage()
self.bgRect = self.background.get_rect()
self.backgroundOnUpdate = self.background.copy()
self.bgRetOnUpdate = self.bgRect.copy()
def resizeUpdateBackground(self):
self.bgRect.center = self.screen.get_rect().center
self.bgRetOnUpdate = self.bgRect.copy()
def zoomUpdateBackground(self, zoomRatio):
self.bgRetOnUpdate.width = self.bgRect.width * zoomRatio
self.bgRetOnUpdate.height = self.bgRect.height * zoomRatio
self.bgRetOnUpdate.width = self.bgRect.width * zoomRatio
self.bgRetOnUpdate.center = self.screen.get_rect().center
self.backgroundOnUpdate = pygame.transform.scale(self.background, (self.bgRetOnUpdate.width, self.bgRetOnUpdate.height))
def drawBackground(self, screen):
screen.blit(self.backgroundOnUpdate, self.bgRetOnUpdate)
def addTestSprite(self):
#self.rootSpriteGroup.add(ButtonSprite(self, initPos=(100, 100), width=100, height=100, dicts= [('on', btn_red_on), ('off', btn_red_off)]))
#self.rootSpriteGroup.add(ButtonSprite(self, initPos=(200, 200), width=100, height=100, dicts= [('on', btn_red_on), ('off', btn_red_off)]))
self.rootSpriteGroup.add(ButtonSprite(self, initPos=(100, 100), width=100, height=100, dicts= [('on', GREEN), ('off', BLUE)]))
self.rootSpriteGroup.add(ButtonSprite(self, initPos=(200, 200), width=100, height=100, dicts= [('on', GREEN), ('off', BLUE)]))
def Update(self, event):
self.Redraw()
return
def Redraw(self):
if self.size[0] == 0 or self.size[1] == 0:
return
if self.size_dirty:
self.screen = pygame.Surface(self.size, 0, 32)
self.resizeUpdateBackground()
self.size_dirty = False
self.screen.fill((0,0,0))
self.drawBackground(self.screen)
w, h = self.screen.get_size()
current_time = pygame.time.get_ticks()
self.previous_time = current_time
self.rootSpriteGroup.update(current_time, self.zoomRatio)
self.rootSpriteGroup.draw(self.screen)
s = pygame.image.tostring(self.screen, 'RGB') # Convert the surface to an RGB string
#img = wx.ImageFromData(self.size[0], self.size[1], s) # Load this string into a wx image
img = wx.ImageFromData(w, h, s) # Load this string into a wx image
#if img.IsOk() is not True:
# return
bmp = wx.BitmapFromImage(img) # Get the image in bitmap form
dc = wx.ClientDC(self) # Device context for drawing the bitmap
dc = wx.BufferedDC( dc)
dc.DrawBitmap(bmp, 0, 0, 1) # Blit the bitmap image to the display
def checkCollide(self, event):
x , y = (event.GetX(),event.GetY())
mousePoint = pygame.sprite.Sprite()
mousePoint.rect = pygame.Rect(x, y, 1, 1)
copoint = pygame.sprite.spritecollide(mousePoint, self.rootSpriteGroup, None)
if copoint:
copoint = copoint[-1]
return copoint
def removeSelectedSprite(self):
if self.selectedSprite:
self.selectedSprite.setSelect(0)
self.selectedSprite = None
def setNewSelectedSprite(self, sprite):
self.removeSelectedSprite()
sprite.setSelect(1)
self.selectedSprite = sprite
def onSelectSprite(self, event, onMouseObj):
if onMouseObj:
if self.selectedSprite:
if onMouseObj != self.selectedSprite:
self.setNewSelectedSprite(onMouseObj)
else:
self.setNewSelectedSprite(onMouseObj)
self.selectedSprite.setLastPos((event.GetX(),event.GetY()))
else:
self.removeSelectedSprite()
def OnMouse(self, event):
onMouseObj = self.checkCollide(event)
event.Skip()
if onMouseObj:
onMouseObj.OnMouseHandler(event)
if not event.GetSkipped():
print "event dropped "
return
if event.LeftDown():
self.onSelectSprite(event, onMouseObj)
elif event.LeftUp():
pass
elif event.RightUp():
self.onSelectSprite(event, onMouseObj)
elif event.RightDown():
self.onSelectSprite(event, onMouseObj)
elif event.Dragging() and event.LeftIsDown():
if self.selectedSprite:
self.selectedSprite.move((event.GetX(),event.GetY()))
def OnPaint(self, event):
self.Redraw()
event.Skip() # Make sure the parent frame gets told to redraw as well
def OnSize(self, event):
self.size = self.GetSizeTuple()
self.size_dirty = True
def Kill(self, event):
self.Unbind(event=wx.EVT_PAINT, handler=self.OnPaint)
self.Unbind(event=wx.EVT_TIMER, handler=self.Update, source=self.timer)
def onZoomIn(self):
self.zoomRatio += 0.2
self.onZoomUpdate()
def onZoomReset(self):
self.zoomRatio = 1
self.onZoomUpdate()
def onZoomOut(self):
if self.zoomRatio > 0.2:
self.zoomRatio -= 0.2
self.onZoomUpdate()
def onZoomUpdate(self):
self.zoomUpdateBackground(self.zoomRatio)
for s in self.rootSpriteGroup.sprites():
s.onZoomUpdate(self.zoomRatio)
class TestFrame ( wx.Frame ):
def __init__( self, parent, fSize ):
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = fSize, style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
fgSizer1 = wx.FlexGridSizer( 2, 1, 0, 0 )
fgSizer1.AddGrowableCol( 0 )
fgSizer1.AddGrowableRow( 0 )
fgSizer1.SetFlexibleDirection( wx.VERTICAL )
fgSizer1.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_ALL )
self.panelMain = MyHmiPanel(self, -1)
fgSizer1.Add( self.panelMain, 1, wx.EXPAND |wx.ALL, 5 )
self.m_panel4 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
bSizer3 = wx.BoxSizer( wx.HORIZONTAL )
self.bZoomIn = wx.Button( self.m_panel4, wx.ID_ANY, u"Zoom In", wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer3.Add( self.bZoomIn, 0, wx.ALL, 5 )
self.bReset = wx.Button( self.m_panel4, wx.ID_ANY, u"Reset", wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer3.Add( self.bReset, 0, wx.ALL, 5 )
self.bZoomOut = wx.Button( self.m_panel4, wx.ID_ANY, u"Zoom Out", wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer3.Add( self.bZoomOut, 0, wx.ALL, 5 )
self.m_panel4.SetSizer( bSizer3 )
self.m_panel4.Layout()
bSizer3.Fit( self.m_panel4 )
fgSizer1.Add( self.m_panel4, 1, wx.EXPAND |wx.ALL, 5 )
self.SetSizer( fgSizer1 )
self.Layout()
self.Centre( wx.BOTH )
self.bZoomIn.Bind( wx.EVT_BUTTON, self.onZoomIn )
self.bReset.Bind( wx.EVT_BUTTON, self.onZoomReset )
self.bZoomOut.Bind( wx.EVT_BUTTON, self.onZoomOut )
def __del__( self ):
pass
def onZoomIn( self, event ):
self.panelMain.onZoomIn()
event.Skip()
def onZoomReset( self, event ):
self.panelMain.onZoomReset()
event.Skip()
def onZoomOut( self, event ):
self.panelMain.onZoomOut()
event.Skip()
if __name__=='__main__':
app = wx.App(redirect=False)
frame = TestFrame(None, (800, 600))
frame.SetPosition((100, 100))
frame.Show()
app.MainLoop()
I solved the problem with :
import wx
from floatcanvas import FloatCanvas
class MyPanel(wx.Panel):
def __init__(self, parent):
super(MyPanel, self).__init__(parent)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.sizer)
# add a canvas
self.Canvas = FloatCanvas.FloatCanvas(self, BackgroundColor = "LIGHT GREY")
self.Canvas.Bind(wx.EVT_SIZE, self.OnSize)
self.sizer.Add(self.Canvas, -1, flag=wx.EXPAND)
# add a toggle button
image_dis = wx.Image('file_disabled.png')
image_ena = wx.Image('file_enabled.png')
img_dis = self.Canvas.AddScaledBitmap(image_dis, (x,-y), Height=image_dis.GetHeight(), Position = 'tl')
img_ena = self.Canvas.AddScaledBitmap(image_ena, (x,-y), Height=image_ena.GetHeight(), Position = 'tl')
img_dis.other = img_ena
img_ena.other = img_dis
img_ena.Visible = False
# bind the toggle button event
img_dis.Bind(FloatCanvas.EVT_FC_LEFT_UP, self.OnToggle)
img_ena.Bind(FloatCanvas.EVT_FC_LEFT_UP, self.OnToggle)
def OnToggle(self, button):
button.other.Visible = True
button.Visible = False
self.Canvas.Draw(True)
def OnSize(self, event):
event.Skip()
wx.CallLater(1, self.Canvas.ZoomToBB)
I can't answer the scaling issue, but an old trick I recall for doing the arbitrary image target hit-checking (no buttons required) goes like this:
1) Create a blank invisible image the same size as the visible one.
2) As you draw targets on the main image, draw an identically shaped "shadow" to the invisible with all the same value pixel (but a unique value for every target). A "handle", if you will.
3) When you get a mouse click on the main image, use the coordinates to get the same pixel from your invisible shadow image. The value will be the handle for the target.
Simple once you hear it, isn't it?