Related
Is there a way to pass a parameter to the event function? When I press the submit button, I receive the following error:
NameError: global name 'filename' is not defined
Here is the full error:
raceback (most recent call last):
File "C:\Users\sbing\OneDrive\Documents\CNM\Spring2018\CIS1250_Python\Labs\Prgm11\geopoint.py", line 60, in submit
file = open(filename.GetValue(), 'r')
NameError: global name 'filename' is not defined
Here is my code:
# handle submit button
def submit(self, event):
file = open(filename.GetValue(), 'r')
contents.SetValue(file.read())
file.close()
def createForm(self):
# establish the wxPython App
app = wx.App()
# establish the wxPython frame
win = wx.Frame(None,title="Closes Neighbor", size = (610,535))
# define the screen widgets
# text controls
filename = wx.TextCtrl(win, pos = (100, 50), size = (210, 25))
latitude = wx.TextCtrl(win, pos = (100, 80), size = (210, 25))
longitude = wx.TextCtrl(win, pos = (100, 110), size = (210, 25))
description = wx.TextCtrl(win, pos = (100, 140), size = (210, 25))
answer = wx.TextCtrl(win, pos = (100, 250), size = (400, 50),
style=wx.TE_MULTILINE | wx.HSCROLL)
answer.SetEditable(False)
messages = wx.TextCtrl(win, pos = (100, 350), size = (400, 50),
style=wx.TE_MULTILINE | wx.HSCROLL)
messages.SetEditable(False)
# static labels
lblTitle = wx.StaticText (win, pos = (200, 25), size = (210,25),
label = "Closest Neighbor", style = wx.ALIGN_CENTER)
lblFilename = wx.StaticText (win, pos = (10, 50), size = (210,25),
label = "Filename:")
lblLatitude = wx.StaticText (win, pos = (10, 80), size = (210,25),
label = "Latitude:")
lblLongitude = wx.StaticText (win, pos = (10, 110), size = (210,25),
label = "Longitude:")
lblDescription = wx.StaticText (win, pos = (10, 140), size = (210,25),
label = "Description:")
lblAnswer = wx.StaticText (win, pos = (10, 250), size = (210,25),
label = "Answer:")
lblMessages = wx.StaticText (win, pos = (10, 350), size = (210,25),
label = "Messages:")
# buttons
submitButton = wx.Button(win, label='Find the Closest Point',
pos = (100, 180), size = (180, 25))
submitButton.Bind(wx.EVT_BUTTON, self.submit)
# get the ball roling
win.Show()
app.MainLoop()
As mentioned in my comment, the easiest way to achieve what you want is to change the reference to self.filename but in answer to the question asked, yes you can.
Use the lambda function.
import wx
class TestFrame(wx.Frame):
def __init__(self, *args):
wx.Frame.__init__(self, *args)
Button = wx.Button(self, -1, "Click me")
Button.Bind(wx.EVT_BUTTON, lambda event: self.OnButton(event, flag1="Parameter", flag2="Something else"))
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(Button,1,0,0)
self.SetSizerAndFit(sizer)
self.Show()
def OnButton(self, event, flag1, flag2):
print ("The Button was pressed")
print ("Flag1 was set to ", flag1)
print ("Flag2 was set to ", flag2)
if __name__ == "__main__":
app = wx.App()
myframe = TestFrame(None, -1, "Button Test")
app.MainLoop()
I want two images placed side-by-side to increase in size when the frame is resize (maximized). How do I achieve that?
import wx
class MyFrame2 ( wx.Frame ):
def __init__( self, parent ):
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
bSizer11 = wx.BoxSizer( wx.HORIZONTAL )
self.m_bitmap3 = wx.StaticBitmap( self, wx.ID_ANY, wx.Bitmap( u"img/im1.jpg", wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer11.Add( self.m_bitmap3, 1, wx.ALL|wx.EXPAND, 5 )
self.m_bitmap4 = wx.StaticBitmap( self, wx.ID_ANY, wx.Bitmap( u"img/im2.jpg", wx.BITMAP_TYPE_ANY ), wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer11.Add( self.m_bitmap4, 1, wx.ALL|wx.EXPAND, 5 )
self.SetSizer( bSizer11 )
self.Layout()
self.Centre( wx.BOTH )
def __del__( self ):
pass
app = wx.App(0)
MyFrame2(None).Show()
app.MainLoop()
Based on your pastebin code
import wx
class MyFrame2 ( wx.Frame ):
def __init__( self, parent ):
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )
bSizer11 = wx.BoxSizer( wx.HORIZONTAL )
self.img1=wx.Image("1.bmp", wx.BITMAP_TYPE_ANY)
self.img2=wx.Image("1.bmp", wx.BITMAP_TYPE_ANY)
self.m_bitmap3 = wx.StaticBitmap( self, wx.ID_ANY, wx.BitmapFromImage(self.img1), wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer11.Add( self.m_bitmap3, 1, wx.EXPAND, 0 )
self.m_bitmap4 = wx.StaticBitmap( self, wx.ID_ANY, wx.BitmapFromImage(self.img2))
bSizer11.Add( self.m_bitmap4, 1, wx.EXPAND, 0 )
self.Bind(wx.EVT_SIZE, self.onResize)
self.SetSizer( bSizer11 )
self.Layout()
self.Centre(wx.BOTH)
def __del__( self ):
pass
def onResize(self, event):
# self.Layout()
frame_size = self.GetSize()
frame_h = (frame_size[0]-10) / 2
frame_w = (frame_size[1]-10) / 2
img1 = self.img1.Scale(frame_h,frame_w)
img2 = self.img2.Scale(frame_h,frame_w)
self.m_bitmap3.SetBitmap(wx.BitmapFromImage(img1))
self.m_bitmap4.SetBitmap(wx.BitmapFromImage(img2))
self.Refresh()
self.Layout()
app = wx.App(0)
MyFrame2(None).Show()
app.MainLoop()
Note: a single self.Bind
the size of the Scale is based on half of the frame size and the images are scaled from the original images each time, otherwise they slowly get more and more distorted.
You will need to bind to wx.EVT_SIZE since that is the event that is fired when you resize the frame. Then in that handler, you will want to update the image's size. You can use the Scale() method from wx.Image to change the image's size. Note that you will probably want to scale the image in such a way that you maintain it's aspect ratio or it will look weird when it gets stretched.
Here's an example based loosely on my tutorial for a Photo Viewer from a few years ago:
import os
import wx
class PhotoCtrl(wx.App):
def __init__(self, redirect=False, filename=None):
wx.App.__init__(self, redirect, filename)
self.frame = wx.Frame(None, title='Photo Control', size=(400, 400))
self.image_loaded = False
self.current_size = self.frame.GetSize()
self.filepath = None
self.panel = wx.Panel(self.frame)
self.Bind(wx.EVT_SIZE, self.onResize)
self.PhotoMaxSize = self.current_size.GetHeight() - 10
self.createWidgets()
self.frame.Show()
def createWidgets(self):
instructions = 'Browse for an image'
img = wx.EmptyImage(240,240)
self.imageCtrl = wx.StaticBitmap(self.panel, wx.ID_ANY,
wx.BitmapFromImage(img))
instructLbl = wx.StaticText(self.panel, label=instructions)
self.photoTxt = wx.TextCtrl(self.panel, size=(200,-1))
browseBtn = wx.Button(self.panel, label='Browse')
browseBtn.Bind(wx.EVT_BUTTON, self.onBrowse)
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.mainSizer.Add(wx.StaticLine(self.panel, wx.ID_ANY),
0, wx.ALL|wx.EXPAND, 5)
self.mainSizer.Add(instructLbl, 0, wx.ALL, 5)
self.mainSizer.Add(self.imageCtrl, 0, wx.ALL, 5)
self.sizer.Add(self.photoTxt, 0, wx.ALL, 5)
self.sizer.Add(browseBtn, 0, wx.ALL, 5)
self.mainSizer.Add(self.sizer, 0, wx.ALL, 5)
self.panel.SetSizer(self.mainSizer)
self.panel.Layout()
def onBrowse(self, event):
"""
Browse for file
"""
wildcard = "JPG files (*.jpg)|*.jpg"
dialog = wx.FileDialog(None, "Choose a file",
wildcard=wildcard,
style=wx.OPEN)
if dialog.ShowModal() == wx.ID_OK:
self.photoTxt.SetValue(dialog.GetPath())
self.onView()
dialog.Destroy()
def scale_image(self):
if self.filepath:
img = wx.Image(self.filepath, wx.BITMAP_TYPE_ANY)
# scale the image, preserving the aspect ratio
W = img.GetWidth()
H = img.GetHeight()
if W > H:
NewW = self.PhotoMaxSize
NewH = self.PhotoMaxSize * H / W
else:
NewH = self.PhotoMaxSize
NewW = self.PhotoMaxSize * W / H
img = img.Scale(NewW,NewH)
return img
def onView(self):
self.filepath = self.photoTxt.GetValue()
img = self.scale_image()
self.imageCtrl.SetBitmap(wx.BitmapFromImage(img))
self.panel.Refresh()
self.image_loaded = True
def onResize(self, event):
print 'resizing'
if self.image_loaded:
if self.current_size != self.frame.GetSize():
self.current_size = self.frame.GetSize()
self.PhotoMaxSize = self.current_size.GetHeight() - 30
img = self.scale_image()
self.imageCtrl.SetBitmap(wx.BitmapFromImage(img))
self.panel.Refresh()
self.panel.Layout()
if __name__ == '__main__':
app = PhotoCtrl()
app.MainLoop()
Note that this seems to scale the image just fine, but the buttons on the bottom are getting chopped off. I'm not sure why and don't really have the time at the moment to diagnose that issue, but in general this is probably the way you want to do it.
self.image2Code:
from tkinter import *
from tkinter import font
from PIL import Image, ImageTk
class App(Tk):
def __init__(self):
Tk.__init__(self)
self.variables()
self.makeUI()
def variables(self):
self.buttonlist = []
self.font = font.Font(family = "Consolas", size = 12, weight = "bold")
def makeUI(self):
self.title("Changing font")
self.geometry("300x300")
self.minsize(200, 200)
self.maxsize(1000, 1000)
self.columnconfigure(0, weight = 1)
self.rowconfigure(0, weight = 1)
self.buttonlist.append(Button(self, height = 2, width = 4, font = self.font))
self.buttonlist[0].grid(row = 0, column = 0, sticky = W+E+S+N, padx = 2, pady = 2)
self.update()
self.image2 = Image.open("1.png")
self.image = ImageTk.PhotoImage(self.image2.resize((self.buttonlist[0].winfo_width(), self.buttonlist[0].winfo_height()), Image.ANTIALIAS))
self.buttonlist[0].configure(image = self.image)
self.buttonlist[0].bind("<Configure>", self.changeimage)
def changeimage(self, *args):
self.update()
#use smaller size
x = self.buttonlist[0].winfo_width()
y = self.buttonlist[0].winfo_height()
x = x if x < y else y
self.image = ImageTk.PhotoImage(self.image2.resize((x, x), Image.ANTIALIAS))
self.buttonlist[0].configure(image = self.image)
def main():
root = App()
root.mainloop()
if __name__ == "__main__":
main()
The problem:
The code works, the problem is that the program is slow on a quite powerful PC. It is making unnecessary calculations. My image is called "1.png".I don't know if I'm using the right approach.
I just started toying around with Matplotlib's Animation capabilities in order to produce a Google Finance looking chart.
I combined two examples I found on the project website (Draggable rectangle exercise, api example code: date_demo.py) and tweaked them a bit to come up with the code listed at the bottom.
While it doesn't look too bad, I would like the top chart (master) update dynamically as the bottom chart (slave) selection is moved around, and not only when the bottom selection is released. How can I do this? I tried to move the self.rect.figure.canvas.draw() bit to the on_motion method, but it seems to interfere with the blit stuff as the bottom selection won't render properly.
So I would assume the solution would be to do the intelligent animation for the bottom chart, i.e., the blit-ing bit, while the top chart is just re-drawn altogether. The issue is that the only way I can redraw anything is through the re-drawing the whole canvas, and this would include the bottom chart. I did find the draw() method for matplotlib.axes, but I can't get it to work. As I said above, preferably I would like to just re-draw the top chart while the bottom one is blit-ed the clever way. Does anyone know how to do this?
Here is my code so far. Please excuse the code, it's a bit untidy.
import datetime
import numpy as np
import sys
import time
import wx
import matplotlib
from matplotlib.figure import Figure
import matplotlib.dates as mdates
import matplotlib.ticker as mtickers
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import matplotlib.patches as mpatches
class DraggableRectangle:
lock = None
def __init__(self, rect, master, xMin, xMax):
self.rect = rect
self.press = None
self.background = None
self.xMax = xMax
self.xMin = xMin
self.master = master
def connect(self):
self.cidpress = self.rect.figure.canvas.mpl_connect('button_press_event', self.on_press)
self.cidrelease = self.rect.figure.canvas.mpl_connect('button_release_event', self.on_release)
self.cidmotion = self.rect.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)
def on_press(self, event):
if event.inaxes != self.rect.axes: return
if DraggableRectangle.lock is not None: return
contains, attrd = self.rect.contains(event)
if not contains: return
x0, y0 = self.rect.xy
self.press = x0, y0, event.xdata, event.ydata
DraggableRectangle.lock = self
canvas = self.rect.figure.canvas
axes = self.rect.axes
self.rect.set_animated(True)
canvas.draw()
self.background = canvas.copy_from_bbox(self.rect.axes.bbox)
axes.draw_artist(self.rect)
canvas.blit(axes.bbox)
def on_motion(self, event):
if DraggableRectangle.lock is not self: return
if event.inaxes != self.rect.axes: return
x0, y0, xpress, ypress = self.press
dx = event.xdata - xpress
dy = 0
if x0+dx > self.xMax:
self.rect.set_x(self.xMax)
elif x0+dx < self.xMin:
self.rect.set_x(self.xMin)
else:
self.rect.set_x(x0+dx)
self.rect.set_y(y0+dy)
canvas = self.rect.figure.canvas
axes = self.rect.axes
canvas.restore_region(self.background)
self.master.set_xlim(self.rect.get_x(), self.rect.get_x() + 92)
axes.draw_artist(self.rect)
canvas.blit(axes.bbox)
def on_release(self, event):
if DraggableRectangle.lock is not self: return
self.press = None
DraggableRectangle.lock = None
self.rect.set_animated(False)
self.background = None
self.rect.figure.canvas.draw()
def disconnect(self):
self.rect.figure.canvas.mpl_disconnect(self.cidpress)
self.rect.figure.canvas.mpl_disconnect(self.cidrelease)
self.rect.figure.canvas.mpl_disconnect(self.cidmotion)
class MplCanvasFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, title='First Chart', size=(800, 700))
datafile = matplotlib.get_example_data('goog.npy')
r = np.load(datafile).view(np.recarray)
datesFloat = matplotlib.dates.date2num(r.date)
figure = Figure()
xMaxDatetime = r.date[len(r.date)-1]
xMinDatetime = r.date[0]
xMaxFloat = datesFloat[len(datesFloat)-1]
xMinFloat = datesFloat[0]
yMin = min(r.adj_close) // 5 * 5
yMax = (1 + max(r.adj_close) // 5) * 5
master = figure.add_subplot(211)
master.plot(datesFloat, r.adj_close)
master.xaxis.set_minor_locator(mdates.MonthLocator())
master.xaxis.set_major_locator(mdates.MonthLocator(bymonth=(1,4,7,10)))
master.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
master.set_xlim(datesFloat[120], datesFloat[120]+92)
master.yaxis.set_minor_locator(mtickers.MultipleLocator(50))
master.yaxis.set_major_locator(mtickers.MultipleLocator(100))
master.set_ylim(yMin, yMax)
master.set_position([0.05,0.20,0.92,0.75])
master.xaxis.grid(True, which='minor')
master.yaxis.grid(True, which='minor')
slave = figure.add_subplot(212, yticks=[])
slave.plot(datesFloat, r.adj_close)
slave.xaxis.set_minor_locator(mdates.MonthLocator())
slave.xaxis.set_major_locator(mdates.YearLocator())
slave.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
slave.set_xlim(xMinDatetime, xMaxDatetime)
slave.set_ylim(yMin, yMax)
slave.set_position([0.05,0.05,0.92,0.10])
rectangle = mpatches.Rectangle((datesFloat[120], yMin), 92, yMax-yMin, facecolor='yellow', alpha = 0.4)
slave.add_patch(rectangle)
canvas = FigureCanvas(self, -1, figure)
drag = DraggableRectangle(rectangle, master, xMinFloat, xMaxFloat - 92)
drag.connect()
app = wx.PySimpleApp()
frame = MplCanvasFrame()
frame.Show(True)
app.MainLoop()
I had a chance to work on this this morning (we are having a 2nd blizzard for the last 3 days). You are right, if you try to redraw the entire figure in the on_motion, it messes up the animation of the yellow rectangle. The key is to also blit the line on the master sub plot.
Try this code out:
import datetime
import numpy as np
import sys
import time
import wx
import matplotlib
from matplotlib.figure import Figure
import matplotlib.dates as mdates
import matplotlib.ticker as mtickers
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import matplotlib.patches as mpatches
class DraggableRectangle:
lock = None
def __init__(self, rect, master, xMin, xMax):
self.rect = rect
self.press = None
self.slave_background = None
self.master_background = None
self.xMax = xMax
self.xMin = xMin
self.master = master
self.master_line, = self.master.get_lines()
def connect(self):
self.cidpress = self.rect.figure.canvas.mpl_connect('button_press_event', self.on_press)
self.cidrelease = self.rect.figure.canvas.mpl_connect('button_release_event', self.on_release)
self.cidmotion = self.rect.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)
def on_press(self, event):
if event.inaxes != self.rect.axes: return
if DraggableRectangle.lock is not None: return
contains, attrd = self.rect.contains(event)
if not contains: return
x0, y0 = self.rect.xy
self.press = x0, y0, event.xdata, event.ydata
DraggableRectangle.lock = self
canvas = self.rect.figure.canvas
axes = self.rect.axes
# set up our animated elements
self.rect.set_animated(True)
self.master_line.set_animated(True)
self.master.xaxis.set_visible(False) #we are not animating this
canvas.draw()
# backgrounds for restoring on animation
self.slave_background = canvas.copy_from_bbox(self.rect.axes.bbox)
self.master_background = canvas.copy_from_bbox(self.master.axes.bbox)
axes.draw_artist(self.rect)
canvas.blit(axes.bbox)
def on_motion(self, event):
if DraggableRectangle.lock is not self: return
if event.inaxes != self.rect.axes: return
x0, y0, xpress, ypress = self.press
dx = event.xdata - xpress
dy = 0
if x0+dx > self.xMax:
self.rect.set_x(self.xMax)
elif x0+dx < self.xMin:
self.rect.set_x(self.xMin)
else:
self.rect.set_x(x0+dx)
self.rect.set_y(y0+dy)
canvas = self.rect.figure.canvas
axes = self.rect.axes
# restore backgrounds
canvas.restore_region(self.slave_background)
canvas.restore_region(self.master_background)
# set our limits for animated line
self.master.set_xlim(self.rect.get_x(), self.rect.get_x() + 92)
# draw yellow box
axes.draw_artist(self.rect)
canvas.blit(axes.bbox)
#draw line
self.master.axes.draw_artist(self.master_line)
canvas.blit(self.master.axes.bbox)
def on_release(self, event):
if DraggableRectangle.lock is not self: return
self.press = None
DraggableRectangle.lock = None
# unanimate rect and lines
self.rect.set_animated(False)
self.master_line.set_animated(False)
self.slave_background = None
self.master_background = None
# redraw whole figure
self.master.xaxis.set_visible(True)
self.rect.figure.canvas.draw()
def disconnect(self):
self.rect.figure.canvas.mpl_disconnect(self.cidpress)
self.rect.figure.canvas.mpl_disconnect(self.cidrelease)
self.rect.figure.canvas.mpl_disconnect(self.cidmotion)
class MplCanvasFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, title='First Chart', size=(800, 700))
datafile = matplotlib.get_example_data('goog.npy')
r = np.load(datafile).view(np.recarray)
datesFloat = matplotlib.dates.date2num(r.date)
figure = Figure()
xMaxDatetime = r.date[len(r.date)-1]
xMinDatetime = r.date[0]
xMaxFloat = datesFloat[len(datesFloat)-1]
xMinFloat = datesFloat[0]
yMin = min(r.adj_close) // 5 * 5
yMax = (1 + max(r.adj_close) // 5) * 5
master = figure.add_subplot(211)
master.plot(datesFloat, r.adj_close)
master.xaxis.set_minor_locator(mdates.MonthLocator())
master.xaxis.set_major_locator(mdates.MonthLocator(bymonth=(1,4,7,10)))
master.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
master.set_xlim(datesFloat[120], datesFloat[120]+92)
master.yaxis.set_minor_locator(mtickers.MultipleLocator(50))
master.yaxis.set_major_locator(mtickers.MultipleLocator(100))
master.set_ylim(yMin, yMax)
master.set_position([0.05,0.20,0.92,0.75])
master.xaxis.grid(True, which='minor')
master.yaxis.grid(True, which='minor')
slave = figure.add_subplot(212, yticks=[])
slave.plot(datesFloat, r.adj_close)
slave.xaxis.set_minor_locator(mdates.MonthLocator())
slave.xaxis.set_major_locator(mdates.YearLocator())
slave.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y'))
slave.set_xlim(xMinDatetime, xMaxDatetime)
slave.set_ylim(yMin, yMax)
slave.set_position([0.05,0.05,0.92,0.10])
rectangle = mpatches.Rectangle((datesFloat[120], yMin), 92, yMax-yMin, facecolor='yellow', alpha = 0.4)
slave.add_patch(rectangle)
canvas = FigureCanvas(self, -1, figure)
drag = DraggableRectangle(rectangle, master, xMinFloat, xMaxFloat - 92)
drag.connect()
app = wx.PySimpleApp()
frame = MplCanvasFrame()
frame.Show(True)
app.MainLoop()
My previous attempt at asking this question was horrible and I had also made some progress, please bear with me, I did not intend to re-ask this so many times, and it is not my style.
Here is the final version: I am resizing a window that contains a DC Client painted bitmap and on the EVT_SIZE event, I am resizing it by re-scaling it (using scale, not rescale) and re-painting the image. The problem is it does not appear as though its respecting the aspect ratio even though I am calculating w/h for it. Also when it grows in height the image is distorted. Finally, when another window passes over it, the image goes white. Any ideas how to fix any of these issues? My window/image class is below:
class TransactionImage(wx.Window):
def __init__(self, parent, fname, name):
wx.Window.__init__(self, parent, name=name)
self.dc = wx.ClientDC(self)
self.load_image(fname)
cursor = wx.StockCursor(wx.CURSOR_MAGNIFIER)
self.SetCursor(cursor)
self.Bind(wx.EVT_SIZE, self.resize_space)
def load_image(self, image):
self.image = wx.Image(image, wx.BITMAP_TYPE_JPEG)
(w, h) = self.image.GetSize()
self.image_ar = w/h
def resize_space(self, size):
(w, h) = self.get_best_size()
self.s_image = self.image.Scale(w, h)
self.bitmap = wx.BitmapFromImage(self.s_image)
self.dc.DrawBitmap(self.bitmap, 0, 0, useMask=False)
# how can I 'refresh this area to make it 'fit'
def get_best_size(self):
(window_width, window_height) = self.GetSizeTuple()
new_height = window_width / self.image_ar
new_size = (window_width, new_height)
return new_size
Also, I am having trouble understanding how to properly use the Client DC. I want to refresh the window area before re-drawing the next image, because if i dont i get weird risiduals and it looks bad. In order to fix this I tried using dc.Clear which cleans the background off. However, doing so on every size call as i would need to causes the image to flash white a million times while im re-sizing. how can i avoid this?
EDIT -
In response to Umyal's comment response - here is a very simplified version of my application. Either way I class my window generator for the images the size handler re-scaling the images causes the image to flicker badly, creating an unappealing artifact. Also, when another frame passes over the application, the image display becomes white, as if been erased.
I was thinking as a way around this - I could implement the solution windows image viewer seems to have, which is the image is only rescaled and repainted when the user lets go of the edge of the frame when resizing it. Problem with that solution is that there is no clear way to detect when the user stops resizing the frame. (wxEVT_SIZE, wxEVT_SIZING)
Here is the simplified application code, you will need to find your own images and the bigger the better. The original image dimentions are 3872 x 2592
# this is required for 'real' math - derive the 'aspect ratio'
from __future__ import division
import wx
class TransactionImage(wx.Window):
def __init__(self, parent, fname, name):
wx.Window.__init__(self, parent, name=name)
self.load_image(fname)
cursor = wx.StockCursor(wx.CURSOR_MAGNIFIER)
self.SetCursor(cursor)
self.Bind(wx.EVT_SIZE, self.resize_space)
self.Bind(wx.EVT_PAINT, self.on_paint)
def load_image(self, image):
self.image = wx.Image(image, wx.BITMAP_TYPE_JPEG)
(w, h) = self.image.GetSize()
self.image_ar = w/h
self.bitmap = wx.BitmapFromImage(self.image)
def resize_space(self, event):
(w, h) = self.get_best_size()
self.s_image = self.image.Scale(w, h)
self.bitmap = wx.BitmapFromImage(self.s_image)
def on_paint(self, event):
self.dc = wx.PaintDC(self)
self.dc.DrawBitmap(self.bitmap, 0, 0, useMask=False)
def get_best_size(self):
(window_width, window_height) = self.GetSizeTuple()
new_height = window_width / self.image_ar
new_size = (window_width, new_height)
return new_size
class OriginalTransactionImage(wx.Window):
def __init__(self, parent, fname, name):
wx.Window.__init__(self, parent, name=name)
self.dc = wx.ClientDC(self)
self.load_image(fname)
cursor = wx.StockCursor(wx.CURSOR_MAGNIFIER)
self.SetCursor(cursor)
self.Bind(wx.EVT_SIZE, self.resize_space)
def load_image(self, image):
self.image = wx.Image(image, wx.BITMAP_TYPE_JPEG)
(w, h) = self.image.GetSize()
self.image_ar = w/h
def resize_space(self, size):
(w, h) = self.get_best_size()
self.s_image = self.image.Scale(w, h)
self.bitmap = wx.BitmapFromImage(self.s_image)
self.dc.DrawBitmap(self.bitmap, 0, 0, useMask=False)
def get_best_size(self):
(window_width, window_height) = self.GetSizeTuple()
new_height = window_width / self.image_ar
new_size = (window_width, new_height)
return new_size
class ImageBrowser(wx.Frame):
def __init__(self, image1, image2, parent=None, id=wx.ID_ANY,
pos=wx.DefaultPosition, title='Image Browser'):
size = (1500, 800)
wx.Frame.__init__(self, parent, id, title, pos, size)
self.CentreOnScreen()
self.panel = wx.Panel(self, wx.ID_ANY)
self.panel.SetBackgroundColour(wx.Colour(191,197,229))
self.main_sizer = wx.BoxSizer(wx.VERTICAL)
self.image_panel = wx.Panel(self.panel, wx.ID_ANY, style=wx.SIMPLE_BORDER)
self.image_panel.SetBackgroundColour(wx.Colour(255, 255, 255))
self.image_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.image_panel.SetSizer(self.image_sizer)
self.load_image_sizer(image1, image2)
self.main_sizer.Add(self.image_panel, 1, wx.GROW|wx.ALIGN_CENTER|wx.ALL, 25)
self.panel.SetSizer(self.main_sizer)
def load_image_sizer(self, image1, image2):
#bitmap1 = OriginalTransactionImage(self.image_panel, image1, 'image1')
#bitmap2 = OriginalTransactionImage(self.image_panel, image2, 'image2')
bitmap1 = TransactionImage(self.image_panel, image1, 'image1')
bitmap2 = TransactionImage(self.image_panel, image2, 'image2')
self.image_sizer.Add(bitmap1, 1, wx.GROW|wx.ALIGN_LEFT|wx.ALL, 20)
self.image_sizer.Add(bitmap2, 1, wx.GROW|wx.ALIGN_RIGHT|wx.ALL, 20)
class IBApp(wx.App):
def OnInit(self):
img1 = "0_3126_image1.jpeg"
img2 = "0_3126_image2.jpeg"
ib = ImageBrowser(img1, img2)
ib.Show()
self.SetTopWindow(ib)
return True
app = IBApp(False, None)
app.MainLoop()
Do not keep a reference to client DC in your window instance(http://docs.wxwidgets.org/2.6/wx_wxclientdc.html) ,neither it is the proper way of drawing over window dc
instead bind to PAINT_EVENT and draw there, below i have shown the things you said add to your class
class TransactionImage(wx.Window):
def __init__(self, parent, fname, name):
self.Bind(wx.EVT_SIZE, self.resize_space)
self.Bind(wx.EVT_PAINT, self.onpaint)
def onpaint(self):
dc = wx.PaintDC(self)
dc.DrawBitmap(self.bitmap, 0, 0, useMask=False)
def resize_space(self, size):
(w, h) = self.get_best_size()
self.s_image = self.image.Scale(w, h)
self.bitmap = wx.BitmapFromImage(self.s_image)
self.Refresh()