创立自定义组件

工具包通常只提供最常见的小部件,如按钮、文本小部件、滚动条、滑块等,没有一个工具包能提供所有可能的小部件。wxPython有很多组件,更多的组件是由客户程序员创立的,咱们能够通过两种形式创立自定义组件:一是批改或加强现有的小部件,二是创立自定义组件。

自定义组件的创立有两种形式:一是批改或加强现有的组件,二是从头开始创立一个自定义组件。

一个超链接组件

第一个例子将创立一个超链接。超链接部件将基于现有的wx.lib.stattext.GenStaticText部件。

#hyperlink.pyimport wxfrom wx.lib.stattext import GenStaticTextimport webbrowserclass Link(GenStaticText):    def __init__(self, *args, **kw):        super(Link, self).__init__(*args, **kw)        self.font1 = wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD, True, 'Verdana')        self.font2 = wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Verdana')        self.SetFont(self.font2)        self.SetForegroundColour('#0000ff')        self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvent)        self.Bind(wx.EVT_MOTION, self.OnMouseEvent)    def SetUrl(self, url):        self.url = url    def OnMouseEvent(self, e):        if e.Moving():            self.SetCursor(wx.Cursor(wx.CURSOR_HAND))            self.SetFont(self.font1)        elif e.LeftUp():            webbrowser.open_new(self.url)        else:            self.SetCursor(wx.NullCursor)            self.SetFont(self.font2)        e.Skip()class Example(wx.Frame):    def __init__(self, *args, **kw):        super(Example, self).__init__(*args, **kw)        self.InitUI()    def InitUI(self):        panel = wx.Panel(self)        vbox = wx.BoxSizer(wx.VERTICAL)        hbox = wx.BoxSizer(wx.HORIZONTAL)        st = GenStaticText(panel, label='Go to web site:')        st.SetFont(wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Verdana'))        hbox.Add(st, flag=wx.LEFT, border=20)        link_wid = Link(panel, label='ZetCode')        link_wid.SetUrl('http://www.zetcode.com')        hbox.Add(link_wid, flag=wx.LEFT, border=20)                vbox.Add(hbox, flag=wx.TOP, border=30)        panel.SetSizer(vbox)        self.SetTitle('A Hyperlink')        self.Centre()def main():    app = wx.App()    ex = Example(None)    ex.Show()    app.MainLoop()if __name__ == '__main__':    main()

<img src="https://mymarkdowm.oss-cn-beijing.aliyuncs.com/markdownimg/image-20201103231151066.png" alt="image-20201103231151066" style="zoom:50%;" />

这个超链接组件是基于一个现有的组件。在这个例子中,咱们不画任何货色,只是应用一个现有的组件,咱们对其进行了一些批改。

from wx.lib.stattext import GenStaticTextimport webbrowser

在这里,咱们导入根底widget,从它那里咱们失去了超链接widget和webbrowser模块。webbrowser 模块是一个规范的 Python 模块。咱们将用它来关上默认浏览器中的链接。

self.SetFont(self.font2)self.SetForegroundColour('#0000ff')

创立一个超链接组件背地的想法很简略,咱们从一个根本的wx.lib.stattext.GenStaticText小组件类继承。所以咱们有了一个文本小组件。而后咱们对它进行一些批改。咱们扭转文字的字体和色彩。

if e.Moving():    self.SetCursor(wx.Cursor(wx.CURSOR_HAND))    self.SetFont(self.font1)

如果咱们将鼠标指针悬停在链接上,就会将字体改为下划线,同时也将鼠标指针改为手标。

elif e.LeftUp():    webbrowser.open_new(self.url)

如果咱们左键点击链接,就会在默认的浏览器中关上链接。

Burning widget

这是一个咱们从头开始创立widget的例子,咱们在窗口底部搁置一个wx.Panel,而后手动绘制整个widget。如果你已经刻录过CD或DVD,你曾经看到过这种widget。

#burning.pyimport wxclass Burning(wx.Panel):    def __init__(self, parent):        wx.Panel.__init__(self, parent, size=(-1, 30), style=wx.SUNKEN_BORDER)        self.parent = parent        self.font = wx.Font(9, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL,            wx.FONTWEIGHT_NORMAL, False, 'Courier 10 Pitch')        self.Bind(wx.EVT_PAINT, self.OnPaint)        self.Bind(wx.EVT_SIZE, self.OnSize)    def OnPaint(self, e):        num = range(75, 700, 75)        dc = wx.PaintDC(self)        dc.SetFont(self.font)        w, h = self.GetSize()        self.cw = self.parent.GetParent().cw        step = int(round(w / 10.0))        j = 0        till = (w / 750.0) * self.cw        full = (w / 750.0) * 700        if self.cw >= 700:            dc.SetPen(wx.Pen('#FFFFB8'))            dc.SetBrush(wx.Brush('#FFFFB8'))            dc.DrawRectangle(0, 0, full, 30)            dc.SetPen(wx.Pen('#ffafaf'))            dc.SetBrush(wx.Brush('#ffafaf'))            dc.DrawRectangle(full, 0, till-full, 30)        else:            dc.SetPen(wx.Pen('#FFFFB8'))            dc.SetBrush(wx.Brush('#FFFFB8'))            dc.DrawRectangle(0, 0, till, 30)        dc.SetPen(wx.Pen('#5C5142'))        for i in range(step, 10*step, step):            dc.DrawLine(i, 0, i, 6)            width, height = dc.GetTextExtent(str(num[j]))            dc.DrawText(str(num[j]), i-width/2, 8)            j = j + 1    def OnSize(self, e):        self.Refresh()class Example(wx.Frame):    def __init__(self, *args, **kwargs):        super(Example, self).__init__(*args, **kwargs)        self.InitUI()    def InitUI(self):        self.cw = 75        panel = wx.Panel(self)        CenterPanel = wx.Panel(panel)        self.sld = wx.Slider(CenterPanel, value=75, maxValue=750, size=(200, -1),            style=wx.SL_LABELS)        vbox = wx.BoxSizer(wx.VERTICAL)        hbox = wx.BoxSizer(wx.HORIZONTAL)        hbox2 = wx.BoxSizer(wx.HORIZONTAL)        hbox3 = wx.BoxSizer(wx.HORIZONTAL)        self.wid = Burning(panel)        hbox.Add(self.wid, 1, wx.EXPAND)        hbox2.Add(CenterPanel, 1, wx.EXPAND)        hbox3.Add(self.sld, 0, wx.LEFT|wx.TOP, 35)        CenterPanel.SetSizer(hbox3)        vbox.Add(hbox2, 1, wx.EXPAND)        vbox.Add(hbox, 0, wx.EXPAND)        self.Bind(wx.EVT_SCROLL, self.OnScroll)        panel.SetSizer(vbox)        self.sld.SetFocus()        self.SetTitle("Burning widget")        self.Centre()    def OnScroll(self, e):        self.cw = self.sld.GetValue()        self.wid.Refresh()def main():    app = wx.App()    ex = Example(None)    ex.Show()    app.MainLoop()if __name__ == '__main__':    main()

<img src="https://mymarkdowm.oss-cn-beijing.aliyuncs.com/markdownimg/image-20201103231548941.png" alt="image-20201103231548941" style="zoom:50%;" />

这个小工具以图形形式显示了一个介质的总容量和咱们可用的空间。该小组件由一个滑块管制。咱们自定义部件的最小值是0,最大值是750。如果咱们达到700,咱们就开始用红色绘制。这通常示意适度焚烧。

w, h = self.GetSize()self.cw = self.parent.GetParent().cw...till = (w / 750.0) * self.cwfull = (w / 750.0) * 700

咱们动静地绘制小部件。窗口越大,焚烧的部件越大。反之亦然。这就是为什么咱们必须计算wx.Panel的大小,并在其上绘制自定义小组件。till参数决定了要绘制的总尺寸。这个值来自于滑块部件。它是整个区域的一个比例。full参数决定了咱们开始用红色绘制的点。留神应用浮点运算。这是为了达到更高的精度。

理论的绘制包含三个步骤。咱们绘制黄色或红色和黄色的矩形。而后咱们画出垂直线,将部件分成几个局部。最初,咱们画出数字,这示意介质的容量。

def OnSize(self, e):       self.Refresh()

每次调整窗口大小时,咱们都会刷新小组件。这将导致小组件从新绘制本人。

def OnScroll(self, e):       self.cw = self.sld.GetValue()    self.wid.Refresh()

如果咱们滚动滑块,咱们就会失去理论值并将其保留到self.cw参数中。当焚烧的widget被绘制时,就会应用这个值。而后,咱们将从新绘制该小组件。

CPU widget

有一些零碎应用程序能够测量系统资源,如温度、内存或CPU耗费。为了使应用程序更有吸引力,咱们创立了专门的组件。

以下是零碎应用程序中常常应用的组件件。

#cpu.pyimport wxclass CPU(wx.Panel):    def __init__(self, parent):        wx.Panel.__init__(self, parent, size=(80, 110))        self.parent = parent        self.SetBackgroundColour('#000000')        self.Bind(wx.EVT_PAINT, self.OnPaint)    def OnPaint(self, e):        dc = wx.PaintDC(self)        dc.SetDeviceOrigin(0, 100)        dc.SetAxisOrientation(True, True)        pos = self.parent.GetParent().GetParent().sel        rect = pos / 5        for i in range(1, 21):            if i > rect:                dc.SetBrush(wx.Brush('#075100'))                dc.DrawRectangle(10, i*4, 30, 5)                dc.DrawRectangle(41, i*4, 30, 5)            else:                dc.SetBrush(wx.Brush('#36ff27'))                dc.DrawRectangle(10, i*4, 30, 5)                dc.DrawRectangle(41, i*4, 30, 5)class Example(wx.Frame):    def __init__(self, *args, **kwargs):        super(Example, self).__init__(*args, **kwargs)        self.InitUI()    def InitUI(self):        self.sel = 0        panel = wx.Panel(self)        centerPanel = wx.Panel(panel)        self.cpu = CPU(centerPanel)        hbox = wx.BoxSizer(wx.HORIZONTAL)        self.slider = wx.Slider(panel, value=self.sel, maxValue=100, size=(-1, 100),              style=wx.VERTICAL | wx.SL_INVERSE)        self.slider.SetFocus()        hbox.Add(centerPanel, 0,  wx.LEFT | wx.TOP, 20)        hbox.Add(self.slider, 0, wx.LEFT | wx.TOP, 30)        self.Bind(wx.EVT_SCROLL, self.OnScroll)        panel.SetSizer(hbox)        self.SetTitle("CPU")        self.Centre()    def OnScroll(self, e):        self.sel = e.GetInt()        self.cpu.Refresh()def main():    app = wx.App()    ex = Example(None)    ex.Show()    app.MainLoop()if __name__ == '__main__':    main()

<img src="https://mymarkdowm.oss-cn-beijing.aliyuncs.com/markdownimg/image-20201103231824854.png" alt="image-20201103231824854" style="zoom:50%;" />

咱们创立一个彩色的面板。而后咱们在这个面板上绘制小矩形。矩形的色彩取决于滑块的值。色彩能够是深绿色或亮绿色。

dc.SetDeviceOrigin(0, 100)dc.SetAxisOrientation(True, True)

这里咱们把默认的坐标系改成了笛卡尔坐标系。这是为了让绘图更直观。

pos = self.parent.GetParent().GetParent().selrect = pos / 5

在这里,咱们失去sizer的值。咱们每列有20个矩形。滑块有100个数字。矩形参数将滑块的值转换为矩形,并以亮绿色绘制。

for i in range(1, 21):       if i > rect:        dc.SetBrush(wx.Brush('#075100'))        dc.DrawRectangle(10, i*4, 30, 5)        dc.DrawRectangle(41, i*4, 30, 5)            else:        dc.SetBrush(wx.Brush('#36ff27'))        dc.DrawRectangle(10, i*4, 30, 5)        dc.DrawRectangle(41, i*4, 30, 5)

这里咱们画40个矩形,每列20个。如果被画的矩形数量大于换算后的矩形值,咱们就用深绿色画,否则就用亮绿色画。