I have an image in a wxPython panel that I want to edit by selecting with Matplotlib RectangleSelector
. I have an Image_Viewer
class that draws the image. I have an Editor
class, where the RectangleSelector
functionality resides. Editor
inherits from Image_Viewer
. Finally, a control panel holds the file selector button. The program starts with a default image loaded through the Image_Viewer
draw function. I can use RectangleSelector
fine. But when I load a new image, which also loads through the Image_Viewer
draw function, all connection to the RectangleSelector
disappears.
I've tried to pare down the code as much as I can, but the whole is part of a multi tabbed notebook, hence the presence of wx.lib.agw.aui
and other possible idiosyncrasies.
import os
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import wx
import wx.lib.agw.aui as aui
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.widgets import RectangleSelector
from matplotlib.patches import Rectangle
# ------------------variables-------------------
# Base path
pth = os.path.dirname(os.path.abspath('__file__'))
# folder for gui graphics
gui_graphics = pth + '/gui_graphics'
# temp file for initial image handling
img_pth = os.path.join(pth, "tmp_img.png")
# Folders, subfolders for processed images
all_cats = pth + '/Cats'
new_cats = all_cats + '/new_cats'
min_img_wndw = 295 # min size of top panels
img_y, img_x = 64, 64 # raw cat size
class Image_Viewer(wx.Panel):
def __init__(self, parent):
super(Image_Viewer, self).__init__(parent)
#load image
im_pth = new_cats + '/av_cat'
files = os.listdir(im_pth)
im_pths = [os.path.join(im_pth, im_name) for im_name in files]
try:
self.img = max(im_pths, key=os.path.getctime)
self.image = cv.imread(self.img)
except:
self.image = np.zeros((img_x, img_y), np.uint8)
self.figure = Figure()
self.draw(self.image)
def draw(self, image):
plt.close(self.figure)
self.figure.clf()
self.axes = self.figure.add_axes([0, 0, 1, 1])
self.axes.axis('off')
self.canvas = FigureCanvas(self, -1, self.figure)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.EXPAND)
self.SetSizer(self.sizer)
self.axes.imshow(image, cmap='gray', vmin=0, vmax=255)
self.canvas.draw()
class Editor(Image_Viewer):
def __init__(self, parent):
super(Editor, self).__init__(parent)
self.RS = RectangleSelector(self.axes, self.on_click,
drawtype='box', useblit=True,
button=[1, 3], # don't use middle button
minspanx=5, minspany=5,
spancoords='pixels',
interactive=True)
#self.canvas.draw()
cid = self.canvas.mpl_connect('MouseEvent', self.on_click)
print(cid)
def on_click(self, eclick, erelease):
print("You clicked")
'eclick and erelease are the press and release events'
x1, y1 = eclick.xdata, eclick.ydata
x2, y2 = erelease.xdata, erelease.ydata
w = int(x2-x1)
h = int(y2 - y1)
class control_Panel(wx.Panel):
def __init__(self, parent, image_panel): #, image_panel, terminal_panel
wx.Panel.__init__(self, parent=parent)
#self.panel = wx.Panel(self, -1, size=(580, 180))
self.screen = image_panel
self.pth = os.path.dirname(os.path.abspath('__file__'))
fileDlgBtn = wx.Button(self, label="Get Cats")
fileDlgBtn.Bind(wx.EVT_BUTTON, self.OnOpen)
def OnOpen(self, event):
with wx.FileDialog(self, "Open image file", wildcard="PNG files (*.png)|*.png",
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
return # the user changed their mind
# Proceed loading the file chosen by the user
pathname = fileDialog.GetPath()
image = cv.imread(pathname)
self.screen.draw(image)
class explorer_panel(wx.Panel):
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
bSplitter = wx.SplitterWindow(self)
image_panel = Editor(bSplitter)
panelThree = control_Panel(bSplitter, image_panel)
bSplitter.SplitHorizontally(image_panel, panelThree)
bSplitter.SetSashGravity(0.5)
bSplitter.SetMinimumPaneSize(min_img_wndw)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(bSplitter, 1, wx.EXPAND)
self.SetSizer(sizer)
class Main(wx.Frame):
def __init__(self):
wx.Frame.__init__(
self,
parent = None,
title = "Borges Infinite Image",
size = (600,550)
)
self.SetIcon(wx.Icon(gui_graphics + '/Abe.png'))
panel = wx.Panel(self)
notebook = aui.AuiNotebook(panel)
explorer = explorer_panel(notebook)
notebook.AddPage(explorer, 'Explorer')
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(notebook, 1, wx.ALL|wx.EXPAND, 5)
panel.SetSizer(sizer)
if __name__ == "__main__":
app = wx.App()
frame = Main()
frame.Show()
app.MainLoop()
I cannot test your code because I don't have wx installed atm, but I'm pretty sure your problem stems from you creating a new figure and new canvas everytime you load a new picture.
I belive you should create one Figure and one set of axes, and then replace the content of this axes with the new picture instead
e.g.
class Image_Viewer(wx.Panel):
def __init__(self, parent):
(...)
self.figure = Figure()
self.axes = self.figure.add_axes([0, 0, 1, 1])
self.axes.axis('off')
self.canvas = FigureCanvas(self, -1, self.figure)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.EXPAND)
self.SetSizer(self.sizer)
self.draw(self.image)
def draw(self, image):
self.axes.cla()
self.axes.imshow(image, cmap='gray', vmin=0, vmax=255)
self.canvas.draw()