How to resize and draw an image using wxpython? - python

I want to load an image, resize it to a given size and after draw it in a specific position in a panel.
All this using wxpython.
How can I do it?
Thanks in advance!

wx.Image has a Scale method that will do the resizing. The rest is normal wx coding.
Here's a complete example for you.
import wx
def scale_bitmap(bitmap, width, height):
image = wx.ImageFromBitmap(bitmap)
image = image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)
result = wx.BitmapFromImage(image)
return result
class Panel(wx.Panel):
def __init__(self, parent, path):
super(Panel, self).__init__(parent, -1)
bitmap = wx.Bitmap(path)
bitmap = scale_bitmap(bitmap, 300, 200)
control = wx.StaticBitmap(self, -1, bitmap)
control.SetPosition((10, 10))
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = wx.Frame(None, -1, 'Scaled Image')
panel = Panel(frame, 'input.jpg')
frame.Show()
app.MainLoop()

First off I think the wxPython Docs and Demos do a great job explaining how to use their libraries, especially because the demos can be played with on the fly to see the affect or you can revert to the original. Here is the Windows link to download all the files:
http://www.wxpython.org/download.php#binaries
That said, here is the example code from the demo:
def runTest(frame, nb, log):
bmp = wx.Image(opj('bitmaps/image.bmp'), wx.BITMAP_TYPE_BMP).ConvertToBitmap()
gif = wx.Image(opj('bitmaps/image.gif'), wx.BITMAP_TYPE_GIF).ConvertToBitmap()
png = wx.Image(opj('bitmaps/image.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap()
jpg = wx.Image(opj('bitmaps/image.jpg'), wx.BITMAP_TYPE_JPEG).ConvertToBitmap()
panel = wx.Panel(nb, -1)
pos = 10
wx.StaticBitmap(panel, -1, bmp, (10, pos), (bmp.GetWidth(), bmp.GetHeight()))
pos = pos + bmp.GetHeight() + 10
wx.StaticBitmap(panel, -1, gif, (10, pos), (gif.GetWidth(), gif.GetHeight()))
pos = pos + gif.GetHeight() + 10
wx.StaticBitmap(panel, -1, png, (10, pos), (png.GetWidth(), png.GetHeight()))
pos = pos + png.GetHeight() + 10
wx.StaticBitmap(panel, -1, jpg, (10, pos), (jpg.GetWidth(), jpg.GetHeight()))
return panel
Here it shows how to load an image and displays it on a panel. There are some objects not explained here, but it should give you the general gist.

If you mean adding the image to a toolbar / listbook / toolbook etc.. you will have to convert it to a bitmap (usually only bitmaps allowed).
As far as i know you can't re-size a bitmap, therefore you will have to resize an image before and then convert it.
Here is a good example http://markandclick.com/1/post/2011/12/wxpython-resize-embedded-bitmap-before-adding-it-as-a-tool.html
Here is a copy from the example:
def getFolderBitmap():
img = folder_icon.GetImage().Rescale(scaleW, scaleH)
return img.ConvertToBitmap()

Related

wx.grid.Grid doesn't load image

I'm trying to use a code in this tutorial, but the result is a grayed cell and no image in the cell (see screenshot). It's been days since I started looking for a solution to add an image to a grid cell and I find this solution the least complicated so far, but it won't work for me. Please, can someone help me with this issue so I can move on with my project? It would be greatly appreciated. Thank you.
Here is the code:
import wx
import wx.grid
class MyApp(wx.App):
def OnInit(self):
frame = wx.Frame(None, -1, title = "wx.Grid - Bitmap example")
grid = wx.grid.Grid(frame)
grid.CreateGrid(1,1)
img = wx.Bitmap(r"E:\Dropbox2\Dropbox\Ubot\Ubot\Python\Magnify\Tkinter Magnify\Tests\python-logo.png", wx.BITMAP_TYPE_PNG)
imageRenderer = MyImageRenderer(img)
grid.SetCellRenderer(0,0,imageRenderer)
grid.SetColSize(0,img.GetWidth()+2)
grid.SetRowSize(0,img.GetHeight()+2)
frame.Show(True)
return True
class MyImageRenderer(wx.grid.PyGridCellRenderer):
def __init__(self, img):
wx.grid.PyGridCellRenderer.__init__(self)
self.img = img
def Draw(self, grid, attr, dc, rect, row, col, isSelected):
image = wx.MemoryDC()
image.SelectObject(self.img)
dc.SetBackgroundMode(wx.SOLID)
if isSelected:
dc.SetBrush(wx.Brush(wx.BLUE, wx.SOLID))
dc.SetPen(wx.Pen(wx.BLUE, 1, wx.SOLID))
else:
dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))
dc.SetPen(wx.Pen(wx.WHITE, 1, wx.SOLID))
dc.DrawRectangleRect(rect)
width, height = self.img.GetWidth(), self.img.GetHeight()
if width > rect.width-2:
width = rect.width-2
if height > rect.height-2:
height = rect.height-2
dc.Blit(rect.x+1, rect.y+1, width, height, image, 0, 0, wx.COPY, True)
app = MyApp(0)
app.MainLoop()
And the result I get:
You can use this image for tests:
I don't know if you are running this in an IDE but if you run it on the command line, you will see all of the warnings and errors. i.e.
wxPyDeprecationWarning: Using deprecated class. Use GridCellRenderer instead.
wx.grid.PyGridCellRenderer.__init__(self)
Traceback (most recent call last):
File "20190519.py", line 30, in Draw
dc.DrawRectangleRect(rect)
AttributeError: 'PaintDC' object has no attribute 'DrawRectangleRect'
Acting on these, because the example is old and outdated, we can replace PyGridCellRenderer with GridCellRenderer and dump the dc.DrawRectangleRect(rect) line altogether. if the function doesn't exist, try not using it, then look for an alternative if that doesn't work.
Edit: that line should have been dc.DrawRectangle(rect)
We end up with this:
import wx
import wx.grid
class MyApp(wx.App):
def OnInit(self):
frame = wx.Frame(None, -1, title = "wx.Grid - Bitmap example")
grid = wx.grid.Grid(frame)
grid.CreateGrid(2,2)
img = wx.Bitmap("wxPython.jpg", wx.BITMAP_TYPE_ANY)
imageRenderer = MyImageRenderer(img)
grid.SetCellRenderer(0,0,imageRenderer)
grid.SetColSize(0,img.GetWidth()+2)
grid.SetRowSize(0,img.GetHeight()+2)
frame.Show(True)
return True
class MyImageRenderer(wx.grid.GridCellRenderer):
def __init__(self, img):
wx.grid.GridCellRenderer.__init__(self)
self.img = img
def Draw(self, grid, attr, dc, rect, row, col, isSelected):
image = wx.MemoryDC()
image.SelectObject(self.img)
dc.SetBackgroundMode(wx.SOLID)
if isSelected:
dc.SetBrush(wx.Brush(wx.BLUE, wx.SOLID))
dc.SetPen(wx.Pen(wx.BLUE, 1, wx.SOLID))
else:
dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))
dc.SetPen(wx.Pen(wx.WHITE, 1, wx.SOLID))
dc.DrawRectangle(rect)
width, height = self.img.GetWidth(), self.img.GetHeight()
if width > rect.width-2:
width = rect.width-2
if height > rect.height-2:
height = rect.height-2
dc.Blit(rect.x+1, rect.y+1, width, height, image, 0, 0, wx.COPY, True)
app = MyApp(0)
app.MainLoop()
Which gives us this:
A full set of downloadable documentation is available here: https://extras.wxpython.org/wxPython4/extras/4.0.4/wxPython-docs-4.0.4.tar.gz
The Demos are here:
https://extras.wxpython.org/wxPython4/extras/4.0.4/wxPython-demo-4.0.4.tar.gz
If you got the error as below, then you probably called setlocale() directly instead of using wxLocale, creating a mismatch between the C/C++ and Windows locales.
Only change the locale by creating wxLocale objects to avoid this!
Use these two lines in the code then it will work fine:
import locale
#after created the grid
locale.setlocale(locale.LC_ALL, 'C')

WXPython Display Images Sometimes do not Display

I am currently using WXPython to do a GUI which displays images. I am currently changing the images about 1 to 2 times per second:
image = image.Scale(scaledHeight, scaledWidth)
image1 = image.ConvertToBitmap()
# Center the image
self.panel.bmp1 = wx.StaticBitmap(self.panel, -1, image1, ((width / 2) - (image.GetWidth() / 2), (height / 2) - (image.GetHeight() / 2)), (image.GetWidth(),image.GetHeight()))
Only problem is periodically the image does not display. I tried out an inefficient solution and copied the image into image1, image2, etc and displayed all of them hoping for the chances for all of them to not display to be lower. Unfortunately, the image will still periodically not display. Is there some sort of buffer I need to use?
Thanks in advance!
It would be convenient if you could provide your code snippet and some more details regarding what you are trying to achieve.
How ever I have created a small example which shows how to change/update images on a panel. The code below basically creates a random number in myThread() class. The number is then sent to the gui() class using publisher-subscriber strategy. The changeImage() of the gui() class checks if the value is less than or equal to 5, then it will display green image on the panel named as self.myPanel, else if the value is greater than 5 then a blue image is displayed.
The images can be downloaded from here: green.bmp blue.bmp
Code: Please note that the image files should be in the same directory in which this script is located.
import wx
import time
from wx.lib.pubsub import setupkwargs
from wx.lib.pubsub import pub
from threading import Thread
import random
class gui(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, None, id, title, size=(100,100))
self.myPanel = wx.Panel(self, -1)
image_file1 = 'green.bmp'
image_file2 = 'blue.bmp'
self.image1 = wx.Image(image_file1, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.image2 = wx.Image(image_file2, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.bitmap = wx.StaticBitmap(self.myPanel, -1)
pub.subscribe(self.changeImage, 'Update')
def changeImage(self, value):
#To destroy any previous image on self.myPanel
if self.bitmap:
self.bitmap.Destroy()
#If the value received from myThread() class is <= 5 then display the green image on myPanel
if value <=5:
self.bitmap = wx.StaticBitmap(self.myPanel, -1, self.image1, (0, 0))
#If the value received from myThread() class is > 5 then display the green image on myPanel
else:
self.bitmap = wx.StaticBitmap(self.myPanel, -1, self.image2, (10, 10))
class myThread(Thread):
def __init__(self):
Thread.__init__(self)
self.daemon = True
self.start()
def run(self):
while True:
number = random.randrange(1,10)
wx.CallAfter(pub.sendMessage, 'Update', value=number)
time.sleep(1)
if __name__=='__main__':
app = wx.App()
frame = gui(parent=None, id=-1, title="Test")
frame.Show()
myThread()
app.MainLoop()

Rescale image when parent is resized in wxPython

I need to be able to rescale an image (in realtime) in a wx.Panel when parent wx.Frame is resized (for example for wxPython for image and buttons (resizable)).
This code now works, the behaviour is like in a standard Photo Viewer : the image fits perfectly the parent window, and the rescaling respects aspect ratio.
import wx
class MainPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1, style=wx.FULL_REPAINT_ON_RESIZE)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.img = wx.Image('background.png', wx.BITMAP_TYPE_PNG)
self.imgx, self.imgy = self.img.GetSize()
def OnPaint(self, event):
dc = wx.PaintDC(self)
dc.Clear()
x,y = self.GetSize()
posx,posy = 0, 0
newy = int(float(x)/self.imgx*self.imgy)
if newy < y:
posy = int((y - newy) / 2)
y = newy
else:
newx = int(float(y)/self.imgy*self.imgx)
posx = int((x - newx) / 2)
x = newx
img = self.img.Scale(x,y, wx.IMAGE_QUALITY_HIGH)
self.bmp = wx.BitmapFromImage(img)
dc.DrawBitmap(self.bmp,posx,posy)
class MainFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, title='Test', size=(600,400))
self.panel = MainPanel(self)
self.Show()
app = wx.App(0)
frame = MainFrame(None)
app.MainLoop()
Before continuing to implement some things, I would to know :
is this the "good way" to do it ?
the FULL_REPAINT_ON_RESIZE might be a bit too much (inefficient), but I couldn't make it without this, do you have ideas to improve this ?
how to track mouse clicks ? Example : I want to track a click in a rectangle (10,20) to (50,50) in the original coordinates of the original image. Should I convert this in new coordinates (because the image has been rescaled!) ? This would mean I should do all things now in a very low-level...
This is not a bad way to do it but there are a couple of things you could easily optimize:
Don't call wxImage::Scale() every time in OnPaint(). This is a slow operation and you should do it only once, in your wxEVT_SIZE handler instead of doing it every time you repaint the window.
Call SetBackgroundStyle(wxBG_STYLE_PAINT) to indicate that your wxEVT_PAINT handler already erases the background entirely, this will reduce flicker on some platforms (you won't see any difference on the others which always use double buffering anyhow, but it will still make repainting marginally faster even there).
As for the mouse clicks, I think you don't have any choice but to translate the coordinates yourself.

wx.StaticBitmap - simple transparency (mask, png, bmp?)

After 1 week of constant failure I'm still not able to do a simple task: Load a png with alpha channel or with white background (in example bellow) and have it maintain its transparency in wx.StaticBitmap.
This I need in a wx.panel later. It should stay like this or similar.
This is one of my approaches (white background):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.loc = wx.Image("intro/image.png",wx.BITMAP_TYPE_PNG).ConvertToBitmap()
z = wx.Mask(self.loc, wx.WHITE)
self.loc.SetMask(z)
self.locopic = wx.StaticBitmap(self, -1, self.loc, (0, 0))
I read tons on this topic. I'm rubbish. Sorry. I think I miss something obvious here. wx.Mask , Transparent images
Update:
I managed to get this far with the example found at WorkinWithImages:
import ImageConversions
...
puFilename = "intro/imagealpha.png"
pilImage = Image.open( puFilename )
pilImageWithAlpha = ImageConversions.WxImageFromPilImage( pilImage, createAlpha=True )
self.puWxBitmap = pilImageWithAlpha.ConvertToBitmap()
self.locopic = wx.StaticBitmap(self, -1, self.puWxBitmap)
This is creating the transparent wx.image from a PNG with alpha channel BUT in wx.StaticBitmap would have an ugly black colour where the transparency should be. This is driving me maaad!!! HELP PLEASE!
If only I would manage to display in the wx.panel that image with the transparency in the right place
Thank you community!
As discussed in the python SO chat:
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.loc = wx.Bitmap("intro/image.png")
def OnPaint(self, evt):
dc = wx.PaintDC(self)
dc.SetBackground(wx.Brush("WHITE"))
# ... drawing here all other images in order of overlapping
dc.DrawBitmap(self.loc, 0, 0, True)
The trick is to draw all overlapping images with the wx.PaintDC.
Additionally it's more convenient to use wx.Bitmap instead of wx.Image(..., wx.BITMAP_TYPE_PNG).ConvertToBitmap() for loading PNGs from the file system.

I'm trying to make a wx.Frame with variable transparancy (based on the png mapped in the erase bk event)

I'm trying to make a special splash screen that is displayed while the application is loading,
it outputs messages of the various components loading and features a progress bar.
The first job I am tackling is mapping a .png image to the frame that will host the splash screen.
import wx
class edSplash(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, size=(410, 410), style=wx.NO_BORDER)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.Center()
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
return
def OnEraseBackground(self, evt):
dc = evt.GetDC()
if not dc:
dc = wx.ClientDC(self)
rect = self.GetUpdateRegion().GetBox()
dc.SetClippingRect(rect)
tempBrush = wx.Brush((0,0,0,0),wx.TRANSPARENT)
print tempBrush
dc.SetBackground(tempBrush)
dc.SetBackgroundMode(wx.TRANSPARENT)
#dc.Clear()
img = wx.Image("splash.png", wx.BITMAP_TYPE_PNG, -1)
bmp = wx.BitmapFromImage(img)
dc.DrawBitmap(bmp, 0, 0, True)
def PushMessage(self, mesage):
print mesage
class edApp(wx.App):
def OnInit(self):
splash = edSplash(None, 'Ed')
self.SetTopWindow(splash)
splash.Show(True)
return True
if __name__ == '__main__':
edApp(redirect=False).MainLoop()
The problem is that dc.Clear() clears to an opaque rectangle, although i have set it's brush and mode to transparent (I think :D). Commenting out dc.Clear() gives me the desired variable transparency based on the .png's alpha channel but the window gathers image noise from the neighboring windows.
How could I get both the .png's transparency and have the background clearing to an transparent brush to keep from gathering image noise?
Maybe you should try putting the background image onto a panel rather than the frame. Here's one way to do it:
http://www.blog.pythonlibrary.org/2010/03/18/wxpython-putting-a-background-image-on-a-panel/

Categories