Рубрики
Без рубрики

wxpython: положить фоновое изображение на панели

Получите практические, реальные навыки Python на наших ресурсах и пути

Автор оригинала: Mike Driscoll.

Вчера я получил запрос на создание графического интерфейса с TKinter или Wxpython, который имел изображение для фона с кнопками сверху. Посмотрев на TKinter, я обнаружил, что это виджет PhotoImage только поддерживает два формата: GIF и PGM (если только я не установил библиотеку визуализации Python). Из-за этого я решил дать wxpython whirl. Вот что я узнал.

Используя некоторые из моих Google-Fu, я нашел Тема на daniweb Это казалось, что это может работать. Я воспроизведу этот пример здесь:

# create a background image on a wxPython panel
# and show a button on top of the image
 
import wx
 
class Panel1(wx.Panel):
    """class Panel1 creates a panel with an image on it, inherits wx.Panel"""
    def __init__(self, parent, id):
        # create the panel
        wx.Panel.__init__(self, parent, id)
        try:
            # pick an image file you have in the working 
            # folder you can load .jpg  .png  .bmp  or 
            # .gif files
            image_file = 'roses.jpg'
            bmp1 = wx.Image(
                image_file, 
                wx.BITMAP_TYPE_ANY).ConvertToBitmap()
            # image's upper left corner anchors at panel 
            # coordinates (0, 0)
            self.bitmap1 = wx.StaticBitmap(
                self, -1, bmp1, (0, 0))
            # show some image details
            str1 = "%s  %dx%d" % (image_file, bmp1.GetWidth(),
                                  bmp1.GetHeight()) 
            parent.SetTitle(str1)
        except IOError:
            print "Image file %s not found" % imageFile
            raise SystemExit
        
        # button goes on the image --> self.bitmap1 is the 
        # parent
        self.button1 = wx.Button(
            self.bitmap1, label='Button1', 
            pos=(8, 8))

app = wx.App(False)

# create a window/frame, no parent, -1 is default ID
# change the size of the frame to fit the backgound images
frame1 = wx.Frame(None, -1, "An image on a panel", 
   size=(350, 400))

# create the class instance
panel1 = Panel1(frame1, -1)
frame1.Show(True)
app.MainLoop()

Моя первая мысль, когда я увидел, что это было что-то вроде: «Это, вероятно, плохое». Почему я так думаю? Ну, парень, который разместил это, использовал WX.Staticbitmap для родителя кнопки. Виджет StaticBitmap не является виджетом контейнера, такой как панель или рамка, поэтому я полагал, что это, вероятно, не очень хорошая идея. Таким образом, я попросил Робин Данн на канал #wxpython IRC, что он думал. Он сказал, что если бы я сделал это, как пример выше, я бы, вероятно, имел такие проблемы обхода вкладки, и такие, и он рекомендовал использовать событие evt_erase_background, чтобы сделать какой-то пользовательский рисунок. Поскольку Робин Данн является создателем WxPython, я закончил этот маршрут, и вот какой код я закончил, используя его совет:

import wx

########################################################################
class MainPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent)
        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
        self.frame = parent
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        
        for num in range(4):
            label = "Button %s" % num
            btn = wx.Button(self, label=label)
            sizer.Add(btn, 0, wx.ALL, 5)
        hSizer.Add((1,1), 1, wx.EXPAND)
        hSizer.Add(sizer, 0, wx.TOP, 100)
        hSizer.Add((1,1), 0, wx.ALL, 75)
        self.SetSizer(hSizer)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
    
    #----------------------------------------------------------------------
    def OnEraseBackground(self, evt):
        """
        Add a picture to the background
        """
        # yanked from ColourDB.py
        dc = evt.GetDC()
                
        if not dc:
            dc = wx.ClientDC(self)
            rect = self.GetUpdateRegion().GetBox()
            dc.SetClippingRect(rect)
        dc.Clear()
        bmp = wx.Bitmap("butterfly.jpg")
        dc.DrawBitmap(bmp, 0, 0)
        
    
########################################################################
class MainFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, size=(600,450))
        panel = MainPanel(self)        
        self.Center()
        
########################################################################
class Main(wx.App):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, redirect=False, filename=None):
        """Constructor"""
        wx.App.__init__(self, redirect, filename)
        dlg = MainFrame()
        dlg.Show()
        
#----------------------------------------------------------------------
if __name__ == "__main__":
    app = Main()
    app.MainLoop()

Вот пример скриншота, используя веселую картину бабочки, я взял на себя летом для моего фонового изображения:

Основной кусок кода для ухода, следующий:

def OnEraseBackground(self, evt):
    """
    Add a picture to the background
    """
    # yanked from ColourDB.py
    dc = evt.GetDC()
        
    if not dc:
        dc = wx.ClientDC(self)
        rect = self.GetUpdateRegion().GetBox()
        dc.SetClippingRect(rect)
    dc.Clear()
    bmp = wx.Bitmap("butterfly.jpg")
    dc.DrawBitmap(bmp, 0, 0)

Я скопировал его из демонстрации Colourdb.py, которая находится в демонстрации WXPYPHON и немного отредактировал его, чтобы сделать его работать для моего приложения. По сути, вы просто связываете панель для EVT_erase_background и в этом обработке, вы захватите контекст устройства (DC), который в этом случае является панелью (я думаю). Я называю его четким способом в основном, потому что в моем реальном приложении я использовал изображение с прозрачностью, и он позволил фоновому кровомуществу. Очищая его, я избавился от кровотечения. Во всяком случае, условные проверки, чтобы увидеть, если DC нет или пустым (я не совсем уверен, какой) и если нет, он обновляет регион (или грязную область, которая является любой частью приложения, которая была «повреждена», перемещаясь другое окно над ним). Затем я снимаю свой образ и использую Drawbitmap, чтобы применить его на задний план. Это вроде фанк, и я не совсем понимаю, что происходит, но это работает.

Я также нашел еще один метод, который я не пытался в этом блоге: http://www.5etdemi.com/blog/Archives/2006/06/making-a-panel-with-a-background-in-wxython/

Не стесняйтесь попробовать их и посмотреть, какой из них работает лучше для вас. Это как метод Робина Данна в том, что он тоже использует DCS, но не тот же тип, который я использую.

Обновление 15.01.2014 : Код в этой статье был первоначально протестирован с использованием WxPython 2.8.12. Он был найден, чем в 2.9+, вам нужно будет удалить следующую строку:

self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)

Если вы этого не сделаете, изображение не отображается правильно. Согласно этому Ответ в Stackoverflow Причина в том, что стиль, wx.bg_style_custom, предотвращает удаление фона.

Вы также можете изменить фоновый стиль в wx.bg_style_erase И это тоже будет работать.