【问题标题】:How to create a transparent frame with visible borders?如何创建具有可见边框的透明框架?
【发布时间】:2011-04-20 17:14:37
【问题描述】:

我想创建一个带有可见边框的透明框架。我根据hasenj 的'shape-frame' (http://hasenj.wordpress.com/2009/04/14/making-a-fancy-window-in-wxpython/) 工作编写的代码如下。但我仍然有问题制作一个。我肯定错过了什么。有人能指出程序有什么问题吗?谢谢。


import wx

class FancyFrame(wx.Frame):
    def __init__(self, width, height):
        wx.Frame.__init__(self, None, style = wx.STAY_ON_TOP | 
                          wx.FRAME_NO_TASKBAR | wx.FRAME_SHAPED,
                          size=(width, height))
        self.SetTransparent(50)
        b = wx.EmptyBitmap(width, height)
        dc = wx.MemoryDC()
        dc.SelectObject(b)
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetPen(wx.Pen('red', 2))
        dc.DrawRectangle(0, 0, width, height)
        dc.SelectObject(wx.NullBitmap)
        self.SetShape(wx.RegionFromBitmap(b))
        self.Bind(wx.EVT_KEY_UP, self.OnKeyDown)
        self.Show(True)

    def OnKeyDown(self, event):
        """quit if user pressEsc"""
        if event.GetKeyCode() == 27: #27 is the Esc key
            self.Close(force=True)
        else:
            event.Skip()

if __name__ == "__main__":
    app = wx.App()
    FancyFrame(300, 300)
    app.MainLoop()

【问题讨论】:

    标签: python wxpython


    【解决方案1】:

    在 MainLoop 之外绘制东西并不是一个好主意。你应该设置你的 wx 应用程序,使它是事件驱动的。应该处理事件 EVT_PAINT 和 EVT_SIZE。 wx.Timer 可用于基本运动。

    import wx
    
    #============================================================================= 
    class DrawPanelDB(wx.Panel):
        def __init__(self, *args, **kwargs):
            wx.Panel.__init__(self, *args, **kwargs)
    
            self.ballPosition = [20, 20]
            self.ballDelta = [1, 1]
    
            self.timer = wx.Timer(self)
            self.Bind(wx.EVT_TIMER, self.OnTime, self.timer)
            self.timer.Start(20)
    
            self.Bind(wx.EVT_PAINT, self.OnPaint)
            self.Bind(wx.EVT_SIZE, self.OnSize)
            self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
    
        #-------------------------------------------------------------------------
        def OnPaint(self, event):
            dc = wx.BufferedPaintDC(self)
            dc.SetBackground(wx.Brush(wx.BLACK))
            dc.Clear()
            dc.DrawCirclePoint(self.ballPosition, 20)
    
        #-------------------------------------------------------------------------
        def OnSize(self, event):
            self.Refresh()
            self.Update()
    
        #-------------------------------------------------------------------------
        def OnEraseBackground(self, event):
            pass # Or None  
    
        #-------------------------------------------------------------------------
        def OnTime(self, event):
            self.ballPosition[0] += self.ballDelta[0]
            self.ballPosition[1] += self.ballDelta[1]
            w, h = self.GetClientSizeTuple()
            if self.ballPosition[0] > w:
                self.ballPosition[0] = w
                self.ballDelta[0] *= -1
            if self.ballPosition[1] > h:
                self.ballPosition[1] = h
                self.ballDelta[1] *= -1
            if self.ballPosition[0] < 0:
                self.ballPosition[0] = 0
                self.ballDelta[0] *= -1
            if self.ballPosition[1] < 0:
                self.ballPosition[1] = 0
                self.ballDelta[1] *= -1       
            self.Refresh()
    
    #============================================================================= 
    class MainWindow(wx.Frame):
        def __init__(self, *args, **kwargs):
            wx.Frame.__init__(self, *args, **kwargs)
            self.panel = DrawPanelDB(self)
            self.Show()
    
    #============================================================================= 
    app = wx.App(False)
    win = MainWindow(None)
    app.MainLoop()
    

    编辑:使用 xor 函数进行非破坏性绘图的 ScreenDC 示例。

    import wx
    
    #============================================================================= 
    class MainWindow(wx.Frame):
        def __init__(self, *args, **kwargs):
            wx.Frame.__init__(self, *args, **kwargs)
    
            self.ballPosition = [20, 20]
            self.lastPosition = None
            self.ballDelta = [1, 1]
    
            self.timer = wx.Timer(self)        
            self.Bind(wx.EVT_TIMER, self.OnTime, self.timer)
            self.timer.Start(20)
    
        #-------------------------------------------------------------------------
        def OnTime(self, event):
            self.ballPosition[0] += self.ballDelta[0]
            self.ballPosition[1] += self.ballDelta[1]
            w, h = wx.DisplaySize()
            if self.ballPosition[0] > w:
                self.ballPosition[0] = w
                self.ballDelta[0] *= -1
            if self.ballPosition[1] > h:
                self.ballPosition[1] = h
                self.ballDelta[1] *= -1
            if self.ballPosition[0] < 0:
                self.ballPosition[0] = 0
                self.ballDelta[0] *= -1
            if self.ballPosition[1] < 0:
                self.ballPosition[1] = 0
                self.ballDelta[1] *= -1       
    
            dc = wx.ScreenDC()
            dc.StartDrawingOnTop()
            dc.SetLogicalFunction(wx.XOR) 
            if self.lastPosition is not None:
                dc.DrawRectangle(self.lastPosition[0], self.lastPosition[1], 15, 15)
            self.lastPosition = (self.ballPosition[0], self.ballPosition[1])
            dc.DrawRectangle(self.ballPosition[0], self.ballPosition[1], 15, 15)    
            dc.EndDrawingOnTop()
    
    #============================================================================= 
    app = wx.App(False)
    win = MainWindow(None)
    app.MainLoop()
    

    编辑:另一种可能性是保留屏幕该部分的备份副本。

    import wx
    
    #============================================================================= 
    class MainWindow(wx.Frame):
        def __init__(self, *args, **kwargs):
            wx.Frame.__init__(self, *args, **kwargs)
    
            self.ballPosition = [20, 20]
            self.lastPosition = None
            self.ballDelta = [1, 1]
            self.patch = wx.EmptyBitmap(20, 20)
    
            self.timer = wx.Timer(self)        
            self.Bind(wx.EVT_TIMER, self.OnTime, self.timer)
            self.timer.Start(20)
    
        #-------------------------------------------------------------------------
        def OnTime(self, event):
            self.ballPosition[0] += self.ballDelta[0]
            self.ballPosition[1] += self.ballDelta[1]
            w, h = wx.DisplaySize()
            if self.ballPosition[0] > w:
                self.ballPosition[0] = w
                self.ballDelta[0] *= -1
            if self.ballPosition[1] > h:
                self.ballPosition[1] = h
                self.ballDelta[1] *= -1
            if self.ballPosition[0] < 0:
                self.ballPosition[0] = 0
                self.ballDelta[0] *= -1
            if self.ballPosition[1] < 0:
                self.ballPosition[1] = 0
                self.ballDelta[1] *= -1       
    
            dc = wx.ScreenDC()
            dc.StartDrawingOnTop()
    
            bdc = wx.MemoryDC(self.patch)
            if self.lastPosition is not None:
                dc.Blit(self.lastPosition[0] - 2, self.lastPosition[1] - 2, 20, 20, bdc, 0, 0)
            bdc.Blit(0, 0, 20, 20, dc, self.ballPosition[0] - 2, self.ballPosition[1] - 2)
            self.lastPosition = (self.ballPosition[0], self.ballPosition[1])
    
            dc.DrawRectangle(self.ballPosition[0], self.ballPosition[1], 15, 15)    
            dc.EndDrawingOnTop()
    
    #============================================================================= 
    app = wx.App(False)
    win = MainWindow(None)
    app.MainLoop()
    

    【讨论】:

    • 感谢您的建议。好吧,我确实需要在屏幕上绘制 ,因为我正在实现一个虚拟鼠标。我打算让程序在屏幕上重复绘制矩形或线条,直到用户按下特殊的一键设备的按钮(这是为手残人士准备的),这表示用户想要移动鼠标移动到屏幕上的位置。
    • @yltang - 我添加了另一个在屏幕上而不是窗口上绘图的示例。我没有找到一种方法如何使屏幕的一部分无效以强制重绘,所以我不得不使用 xor 函数。
    【解决方案2】:

    我建议创建一个透明窗口 (window.SetTransparent(0)),并在其上放置一个您自己绘制的位图(例如,参见 this answer)。

    然后,您将为鼠标移动设置一个事件处理程序,并使用光标在窗口中移动。还要设置一个定时器,这样如果鼠标在 x 秒内没有移动,你就可以淡出位图。

    【讨论】:

    • 谢谢瑞恩。我已经根据 hasenj 的工作编写了一些代码。但是,我仍然无法在透明矩形上显示红色边框。由于系统不允许我将整个程序(大约 30 行)粘贴到此处。是否有可能我将我的程序发送给您,您可能会检查它有什么问题?谢谢。
    • 抱歉,系统很奇怪。如果我键入“enter”键,我的消息将被保存并发布,就像我单击“添加评论”(或“保存编辑”)按钮一样。因此,我不能添加空行。此外,如果我复制我的程序并将其粘贴到此处(系统确实允许我粘贴),如果我单击“添加评论”(或“保存编辑”)按钮,则不会发生任何事情——我无法发布消息。我能做什么?
    • @yltang - 我并不是要将代码放在评论中,而是在顶部编辑您的问题。
    猜你喜欢
    • 2020-02-15
    • 1970-01-01
    • 2015-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-24
    • 1970-01-01
    相关资源
    最近更新 更多