2014-01-25 41 views
0

我想提出这样一个形象的按钮(可调整大小)一个wx.PanelwxPython的图像和

  • 动物应该是“按钮,”所以,如果我点击他们,他们的形象变化,如果我reclick,图像恢复正常(因此可以将动物视为简单BitmapToggleButtons,作为建议的另一个问题在这里SO)

  • 这个小组应该调整/重新调整(所有所有的孩子图片/的ToggleButtons呢! )如果父级wx.Panel的大小调整为小一些,例如保持纵横比勒(如会做标准Windows照片查看http://res1.windows.microsoft.com/resbox/en/windows%207/main/7eaf462a-86dd-42d2-a789-7413f5472dae_63.jpg

我还是有点失落的:如何实现这样的点击(与切换按钮)和rescalable帆布?

enter image description here

编辑:我开始与一些卓有成效这里Rescale image when parent is resized in wxPython,但现在我完全陷入有关如何继续(?直接DC绘画检测点击,更新按钮),这就是为什么赏金。

+0

我应该使用FloatCanvas吗? – Basj

回答

0

我解决了这个问题:

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) 
2

你将不得不实施你自己的命中测试,即能够确定每个动物的位置 - 这是困难的部分,wxWidgets中没有任何东西可以帮助你做到这一点。其余部分相对简单,您甚至可以使用现有的wxMouseEventsManager来避免自己编写样板代码(但如果不能,您至少可以查看其实现,完全在wxWidgets中完成,以查看你需要做什么)。

+0

我想创建每个动物为'BitmapToggleButton',那么很容易切换和更新图像。 对我来说,真正的问题是:如何重新调整当父面板的大小调整所有(背景图片+每个动物的按键)... – Basj

+0

如果我这样做,我肯定会用绘制整个事情和处理的鼠标去事件我自己。重新调整整个事物的需要会使定位实际的切换按钮变得很痛苦。考虑到如果将画布的大小调整为大小的一半,则任何在奇数像素*上具有边缘的动物“按钮”都不能被正确定位,因为它现在必须位于半像素位置,我不认为你可以在wx中完成(据我所知)。你没有足够的动物,你应该能够逃脱没有像quadtrees之类的任何花哨的东西。 –

+0

如何用较小的尺寸重新绘制现有的StaticBitmap?我应该重做一个新的StaticBitmap调用吗?那么最后,我会有成千上万的StaticBitmap,不是吗? – Basj

1

根据已写入的内容,您可能需要查看FloatCanvas(它位于wxPython库中)。

如果您完成了大部分代码库,则可以使用命中测试,这非常简单。只需将[x] [y]坐标作为具有BitmapTogglebutton作为其值的键的字典。

下面是一些代码,做类似的东西(它已经有一段时间,因为我用的wxPython所以它可能不是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 
1

enter image description here enter image description here enter image description here

我做了一些最近的练习代码。它可能以某种方式符合您的要求。 代码很丑陋,因为我是python的新手。

支持:在与研究背景拖动

  • 图像动画

    1. 图像双击
    2. 背景和图像也调整大小

    注意:

    1. 你需要有pygame来运行代码
    2. 您可以通过更换PyGamePseudoImage()
    3. 图像坐标调整加载真实图像不够流畅,同时放大/缩小

    代码:

    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() 
    
  • +0

    感谢@ user3239580,我现在就去看这个。 ()我在wxFlexGridSizer :: AddGrowableCol()中有一个'wx._core.PyAssertionError:C++声明'!m_cols || idx <(size_t)m_cols“在.. \ .. \ src \ common \ sizer.cpp(1980) :无效列索引'你知道原因吗?) – Basj

    +0

    对不起,请删除这行“fgSizer1.AddGrowableCol(1)”,虽然它与我的wxpython 2.8.12.1和Python 2.7一起工作。3 –

    +0

    然后我得到另一个'IndexError:元组索引超出范围',如果我删除这条线。你有好主意吗 ? – Basj

    0

    我不能回答的比例问题,而是一个老把戏我记得做任意图像目标命中检查(无需按钮)是这样的:

    1)创建空白不可见图像的尺寸与可见一个相同。

    2)当你在主图像上绘制目标,绘制一个形状相同的“影子”,以无形的所有相同的像素值(但为每个目标的唯一值)。一个“手柄”,如果你愿意的话。

    3)当你在主图像上点击鼠标,使用坐标从无形的阴影图像得到相同的像素。该值将成为目标的句柄。

    简单,一旦你听到它,是不是?