I'm a beginner at python and programming for that matter and trying to implement a rudimentary image viewer and having trouble to update the sub panel, in which the image should be shown. When I feed an image to the panel at the start of the program, it shows correctly. However, when I try to update the panel with another image via the open file- or open directory-dialog it does not display correctly (see screenshot).
From what I read so far about updating panels (wxpython refresh window on button press,wxPython - change panel by button,How do you force refresh of a wx.Panel?) it is not enough to just recall the function responsible for creating the sub panel, but I seem to oversee or miss something elementary because I cannot get any of the proposed solutions to work in my code.
I also had a look at this photo viewer tutorial at Mouse vs Python, but I could't get it to work in my program.
Below is the code I have so far. It also reproduces the faulty result which you can see on the screenshot. I appreciate any constructive help and feedback you can give me.
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()
First of all, thanks again to pyrrhoofcam for pointing me into the right direction. I worked out a solution that works, but I don't know if it is "good" code. I simply defined the image as an static class attribute which can be changed through the additional updateImage() method within the same class. The code of the panel class comicPagePanel() looks like this now:
#====================================================================
# 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))
Now, if the user wants to open a new image via the file dialog, the updateImage() method is called. This call looks like this:
#----------------------------------------------------------
# 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