【问题标题】:wxPython: Update image / bitmap in sub panelwxPython:更新子面板中的图像/位图
【发布时间】:2020-09-20 15:52:43
【问题描述】:

我是 python 的初学者并为此进行了编程,并试图实现一个基本的图像查看器,并且在更新应该显示图像的子面板时遇到了麻烦。当我在程序开始时将图像输入面板时,它会正确显示。但是,当我尝试通过打开文件或打开目录对话框使用另一个图像更新面板时,它无法正确显示(请参阅screenshot)。

从我目前所读到的关于更新面板的内容 (wxpython refresh window on button press,wxPython - change panel by button,How do you force refresh of a wx.Panel?) 仅仅回忆负责创建子面板的函数是不够的,但我似乎监督或错过了一些基本的东西因为我无法在我的代码中使用任何建议的解决方案。

我还在 Mouse vs Python 中查看了这个 photo viewer tutorial,但我无法让它在我的程序中工作。

下面是我到目前为止的代码。它还重现了您可以在screenshot 上看到的错误结果。感谢您给我的任何建设性帮助和反馈。

import glob
import wx
import os

#====================================================================
class textEditor(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent):

        wx.Panel.__init__(self, parent)

        sizer = wx.BoxSizer(wx.VERTICAL)

        speeBubImg = wx.StaticBox(self, wx.ID_ANY, "Text Editor")
        sizer.Add(speeBubImg, wx.ID_ANY, flag=wx.EXPAND|wx.ALL)

        self.SetSizer(sizer)

#====================================================================
class comicPagePanel(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent, imgsPath):

        wx.Panel.__init__(self, parent)

#        print('comicPagePanel', imgsPath)

        # create the static box with the panel description...
        comPageStatBox = wx.StaticBox(self, wx.ID_ANY, "Comic Page")
        # ...and asign a sizer to it
        comPageStatBoxSizer = wx.StaticBoxSizer(comPageStatBox, wx.VERTICAL)

         # Feeding the panel with an image when starting the program works
#        imgsPath.append('someImage.jpg')

        if imgsPath:

#            print('comicPagePanel_if-imgPath', imgsPath)

            # create the image box
            comPageBox = wx.Bitmap(wx.Image(imgsPath[0], wx.BITMAP_TYPE_ANY))

            img = comPageBox.ConvertToImage()

            iW = img.GetWidth()
            iH = img.GetHeight()
            imgMaxSize = 1000

            if iW > iH:
                NewW = imgMaxSize
                NewH = imgMaxSize * iH / iW
            else:
                NewH = imgMaxSize
                NewW = imgMaxSize * iW / iH

            img = wx.Bitmap(img.Scale(NewW,NewH))

            # create another sizer for the actual image box
            comPageBoxSizer = wx.StaticBitmap(self, wx.ID_ANY, img)

            # add the image box sizer to the sizer of the
            # static box with the panel description
            comPageStatBoxSizer.Add(comPageBoxSizer, wx.ID_ANY, wx.EXPAND|wx.ALL)

        # create a main sizer which stretches all other sizers to the
        # size of the subpanel
        main_sizer = wx.BoxSizer()

        # add the static box with the image box that is nested in it
        # to the main sizer
        main_sizer.Add(comPageStatBoxSizer, wx.ID_ANY, wx.EXPAND|wx.ALL)

        # fit the main sizer to the subpanel
        self.SetSizer(main_sizer)

#====================================================================
class comicPageViewer(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent, imgsPath):

        wx.Panel.__init__(self, parent)

        # laying out the grid for the image panel and the ctrl-buttons
        sizer = wx.GridBagSizer(2, 4)

        # the image viewing panel
        comPage = comicPagePanel(self, imgsPath)
        sizer.Add(comPage, pos=(0, 0), span=(1, 4),
                  flag=wx.EXPAND|wx.ALL, border=5)

        # the ctrl-buttons
        butPrev = wx.Button(self, label="Previous Page")
        butNext = wx.Button(self, label="Next Page")
        butOCR = wx.Button(self, label="Find Text/OCR")
        butSaveTxt = wx.Button(self, label="Save Current Text(s)")


        sizer.Add(butPrev, pos=(1, 0), flag=wx.EXPAND)
        sizer.Add(butNext, pos=(1, 1), flag=wx.EXPAND)
        sizer.Add(butOCR, pos=(1, 2), flag=wx.EXPAND)
        sizer.Add(butSaveTxt, pos=(1, 3), flag=wx.EXPAND)


        sizer.AddGrowableCol(0)
        sizer.AddGrowableCol(1)
        sizer.AddGrowableCol(2)
        sizer.AddGrowableCol(3)

        sizer.AddGrowableRow(0)

        self.SetSizer(sizer)

#====================================================================
class mainPanel(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent, imgsPath):

        wx.Panel.__init__(self, parent)

#        print(imgsPath)

        # Create a sub panel left and right
        lSubPan = comicPageViewer(self, imgsPath)
        rSubPan = textEditor(self)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(lSubPan, wx.ID_ANY, wx.EXPAND|wx.ALL)
        sizer.Add(rSubPan, wx.ID_ANY, wx.EXPAND|wx.ALL)
        self.SetSizer(sizer)

#====================================================================
class mainFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.createPanel()
        self.Maximize(True)
        self.Show()

    #----------------------------------------------------------  
    def createPanel(self):

        imgsPath = []

        self.CreateStatusBar()
        self.createMenu()
        panel = mainPanel(self, imgsPath)
    #----------------------------------------------------------
    def createMenu(self):      

        # create the menu bar
        menuBar = wx.MenuBar()

        # create a file menu
        fileMenu = wx.Menu()

        opFileBut = wx.MenuItem(fileMenu, wx.ID_ANY, '&Open File', 'Open a Single File')
        fileMenu.Append(opFileBut)

        # bind the open button to the on_open_directory event
        fileMenu.Bind(wx.EVT_MENU, self.onOpenFile, opFileBut)

        # add an open directory Button to the file menu
        opDirBut = wx.MenuItem(fileMenu, wx.ID_ANY, 'Open &Directory', 'Open Working Directory')
        fileMenu.Append(opDirBut)

        # bind the open button to the on_open_directory event
        fileMenu.Bind(wx.EVT_MENU, self.onOpenDirectory, opDirBut)

        # add a line separator to the file menu
        fileMenu.AppendSeparator()

        # add a quit button to fileMenu
        quitBut = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit', 'Exit the Programm')
        fileMenu.Append(quitBut)

        # connect the quit button to the actual event of quitting the app
        fileMenu.Bind(wx.EVT_MENU, self.onQuit, quitBut)

        # call onQuit if the app is closed via x in title bar (in order to do some cleaning up)
        self.Bind(wx.EVT_CLOSE, self.onQuit)

        # give the menu a title
        menuBar.Append(fileMenu, "&File(s)")

        # connect the menu bar to the frame        
        self.SetMenuBar(menuBar)

    #----------------------------------------------------------
    def onOpenFile(self, event):

        with wx.FileDialog(self, "Choose a File",
                          style=wx.FD_DEFAULT_STYLE) as fDlg:
            if fDlg.ShowModal() == wx.ID_OK:

                imgPath = glob.glob(os.path.join(fDlg.GetPath()))

            if imgPath:
                comicPagePanel(self, imgPath)

    #----------------------------------------------------------
    def onOpenDirectory(self, event):

        with wx.DirDialog(self, "Choose a Directory",
                          style=wx.DD_DEFAULT_STYLE) as dDlg:
            if dDlg.ShowModal() == wx.ID_OK:

                imgsPath = glob.glob(os.path.join(dDlg.GetPath(), '*.jpg'))

            if imgsPath:
                comicPagePanel(self, imgsPath)

    #----------------------------------------------------------
    def onQuit(self, event):
        # get the frame's top level parent and close it
        wx.GetTopLevelParent(self).Destroy()


#======================
# Start GUI
#======================

if __name__ == '__main__':
    app = wx.App()
    mainFrame(None, title="Text from Comic Pages")
    app.MainLoop()

【问题讨论】:

    标签: python bitmap wxpython panel


    【解决方案1】:

    首先,只要 wxpython 在左上角放一些东西,就意味着它不知道该放哪里,我想你已经解决了,但知道它很有用。这确实意味着您的 wxpython 对象存在并且可以被渲染,这至少是一些东西。

    它第二次不起作用的原因是这段代码:

    def onOpenFile(self, event):
    
        with wx.FileDialog(self, "Choose a File",
                          style=wx.FD_DEFAULT_STYLE) as fDlg:
            if fDlg.ShowModal() == wx.ID_OK:
    
                imgPath = glob.glob(os.path.join(fDlg.GetPath()))
    
            if imgPath:
                comicPagePanel(self, imgPath)
    

    创建一个新的comicPagePanel 对象(在最后一行),但它是一个新的,与已经创建的完全分开。新的与任何尺寸无关,它只是框架的一个孩子。所以它出现在左上角,因为没有与之关联的布局。

    您的代码结构无法满足您的需求。为了更新 sizer 中的图像,您需要获取此对象

    comPageBoxSizer = wx.StaticBitmap(self, wx.ID_ANY, img)
    

    并对其进行如下操作

    comPageBoxSizer.SetBitmap( ... new bitmap object here ... )
    

    注意,您还应该更改此变量的名称。它不是一个sizer,它是一个StaticBitmap。你可以通过询问 python 它的类型是什么来检查这个

    print(type(comPageBoxSizer))
    

    在名称中包含类型是很合理的,并且可能会有所帮助,但如果类型错误,则绝对没有帮助。

    一个可行的解决方案将在您的comicPagePanel 类中具有一个新方法,该方法具有新图像的参数,然后更新嵌入在布局结构中的位图。

    我希望从这个描述中你能明白如果你在运行开始时声明图像,它为什么会起作用。图像被适当地放置在布局结构中。如果您稍后添加它,它将与该布局完全分开。

    【讨论】:

      【解决方案2】:

      首先,再次感谢 pyrrhoofcam 为我指明了正确的方向。我制定了一个可行的解决方案,但我不知道它是否是“好”的代码。我只是将图像定义为静态类属性,可以通过同一类中的附加 updateImage() 方法进行更改。现在面板类 ComicPagePanel() 的代码如下所示:

      #====================================================================
      # the upper left panel that shows the image of the comic page
      class comicPagePanel(wx.Panel):
      
          # create an "empty" image as placeholder;
          # it will be replaced by an actual image via the open file dialog
          comPageImg = wx.Image(1,1)
      
          #----------------------------------------------------------------------
          def __init__(self, parent):
              # Constructor
              wx.Panel.__init__(self, parent)
      
              # create the static box with the panel description...
              comPageStatBox = wx.StaticBox(self, wx.ID_ANY, "Comic Page")
              # ...and asign a sizer to it
              comPageStatBoxSizer = wx.StaticBoxSizer(comPageStatBox, wx.VERTICAL)
      
              # get the background color of the panel
              [panR, panG, panB] = self.GetBackgroundColour()[:3]
      
              # set the color of the image placeholder to the background color of the panel
              comicPagePanel.comPageImg.Replace(0,0,0,panR,panG,panB)
      
              # convert the image placeholder to a static bitmap
              comicPagePanel.comPageImg = wx.StaticBitmap(self, wx.ID_ANY, wx.Bitmap(comicPagePanel.comPageImg))
      
              # add the image placeholder to the sizer of the
              # static box with the panel description; meaning, placing it within the static box
              comPageStatBoxSizer.Add(comicPagePanel.comPageImg, wx.ID_ANY, wx.EXPAND|wx.ALL)
      
              # create a main sizer which stretches all other sizers to the
              # size of the subpanel
              main_sizer = wx.BoxSizer()
      
              # add the static box with the image that is nested in it
              # to the main sizer
              main_sizer.Add(comPageStatBoxSizer, wx.ID_ANY, wx.EXPAND|wx.ALL)
      
              # fit the main sizer to the subpanel
              self.SetSizer(main_sizer)
      
          #----------------------------------------------------------------------
          # the update method of the comic page image viewer
          def updateImage(self, imgsPath):
      
              # get the new image
              newImg = wx.Image(imgsPath[0], wx.BITMAP_TYPE_ANY)
      
              # get the display resolution in order to fit the image into the panel
              [disX, disY] = wx.GetDisplaySize()
      
              # determine the approximate size of the image panel
              disX = disX/2-50
              disY = disY-175
      
              # get the size of the new image
              [iW, iH] = newImg.GetSize()
      
              # scale the image proportionally        
              if not iH < disY and iW > iH:
                  NewW = disX
                  NewH = disX * iH / iW
                  # scaling the page image
                  newImg = newImg.Scale(NewW,NewH)
              elif not iW < disX:
                  NewH = disY
                  NewW = disY * iW / iH
                  # scaling the page image
                  newImg = newImg.Scale(NewW,NewH)
      
              # replace the old image in the panel with the new one
              comicPagePanel.comPageImg.SetBitmap(wx.Bitmap(newImg))
      

      现在,如果用户想通过文件对话框打开新图像,则调用 updateImage() 方法。此调用如下所示:

      #----------------------------------------------------------
      # open a file dialog
      def onOpenFile(self, event):
      
          with wx.FileDialog(self, "Choose a File",
                            style=wx.FD_DEFAULT_STYLE) as fDlg:
              if fDlg.ShowModal() == wx.ID_OK:
      
                  imgPath = glob.glob(os.path.join(fDlg.GetPath()))
      
              if imgPath:
                  comicPagePanel.updateImage(self, imgPath)
              else:
                  return
      

      【讨论】:

        猜你喜欢
        • 2012-03-22
        • 2013-02-12
        • 2012-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多