wxpython rearrange bitmap on resize - python

I have this wxpython code, where I am displaying image along with text. I am using flexgridsizer for layout management. I want when I resize the window, images should reshuffle as per the size like if there is more room than the column and row should expand, also I see flicker while regenerating images.
Please suggest, if there is anyother better way to do this
import wx
ID_MENU_REFRESH = wx.NewId()
#dummy description of image
imglist =['One','Two','Three','Four','Five','Six','Seven','Eight','Nine','Ten']
class Example(wx.Frame):
def __init__(self, *args, **kwargs):
super(Example, self).__init__(*args, **kwargs)
mb = wx.MenuBar()
fMenu = wx.Menu()
fMenu.Append(ID_MENU_REFRESH, 'Refresh')
mb.Append(fMenu, '&Action')
self.SetMenuBar(mb)
self.Bind(wx.EVT_MENU, self.refreshApps, id=ID_MENU_REFRESH)
#storing the thumb image in memory
self.bmp=wx.Image('img/myimg.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap()
self.panelOne = wx.Panel(self)
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.panelOne.SetSizer(sizer)
self.panelOne.Layout()
self.myimg_holder={}
self.showThumb(imglist)
self.SetTitle('Example')
self.Centre()
self.Show(True)
def refreshApps(self,event=None):
#remove
self.showThumb()
#repaint
self.showThumb(imglist)
def showThumb(self,thumblist=None):
if not thumblist:
for child in self.panelOne.GetChildren():
child.Destroy()
self.myimg_holder.clear()
self.panelOne.Layout()
return
vbox = wx.BoxSizer(wx.VERTICAL)
hbox = wx.BoxSizer()
gs = wx.FlexGridSizer(8, 6, 10, 20)
#blank text holder for padding
gs.Add(wx.StaticText(self.panelOne),flag=wx.ALL|wx.EXPAND, border=2)
vzis = []
for num,app in enumerate(thumblist):
vzis.append(wx.BoxSizer(wx.VERTICAL))
appid = wx.StaticBitmap(self.panelOne,wx.ID_ANY, self.bmp, (5,5), (self.bmp.GetWidth()+5, self.bmp.GetHeight()),name=app.strip())
vzis[num].Add(appid,0, wx.ALIGN_CENTER)
self.myimg_holder[appid]=app
vzis[num].Add(wx.StaticText(self.panelOne,-1,app),0, wx.ALIGN_CENTER,border=1)
for i in range(len(thumblist)):
if i in [4,8,12]:
gs.Add(wx.StaticText(self.panelOne),flag=wx.ALL, border=2)
gs.Add(wx.StaticText(self.panelOne),flag=wx.ALL, border=2)
gs.Add(vzis[i],flag=wx.ALL, border=1)
else:
gs.Add(vzis[i],flag=wx.ALL, border=1)
vbox.Add(wx.StaticText(self.panelOne),flag=wx.ALL, border=4)
vbox.Add(gs, proportion=1, flag=wx.ALL)
vbox.Add(wx.StaticText(self.panelOne),flag=wx.ALL, border=4)
self.panelOne.SetSizer(vbox)
self.panelOne.Layout()
def main():
ex = wx.App()
frame = Example(None)
frame.Show()
ex.MainLoop()
if __name__ == '__main__':
main()

If you want the images to rearrange themselves, then you don't want to use the FlexGridSizer. What you want is the WrapSizer:
http://wxpython.org/Phoenix/docs/html/WrapSizer.html
http://www.blog.pythonlibrary.org/2014/01/22/wxpython-wrap-widgets-with-wrapsizer/
If you want the bitmaps themselves to resize when you're resizing the frame, then you'll have to do that yourself. The sizers don't do that automatically. They do resize normal widgets, but not images. You would probably need to catch EVT_SIZE and resize the bitmaps as appropriate if you wanted to go that route.
To reduce flicker, you'll probably want to take a look at the Freeze and Thaw methods.

Related

wxPython's wx.grid.Grid() won't come fully into view

Issue:
I'm experiencing an issue where a function that simply creates a Grid() works when called in one place, but not another place. When it is called from the "other," non-working place, it does create a very small square in the corner of the window. At this time, I don't understand why, and I'm hoping someone can help.
Code: (feel free to copy paste this into a text editor and give it a go!)
import wx
import wx.grid as gridlib
class MainFrame(wx.Frame):
def __init__(self, parent, title):
super(MainFrame, self).__init__(parent, title=title,
size=(350, 250))
self.init_ux()
self.main_grid = None
def init_ux(self):
menu_bar = wx.MenuBar()
file_menu = wx.Menu()
file_menu.AppendSeparator()
menu_bar.Append(file_menu, '&File')
# add open file menu
open_menu = wx.Menu()
my_btn = open_menu.Append(wx.ID_ANY, 'button description')
# append open_menu to the file_menu
file_menu.Append(wx.ID_OPEN, '&Open', open_menu)
self.SetMenuBar(menu_bar)
self.Bind(wx.EVT_MENU, lambda event: self.open_dialog(data="i love string literals."), my_btn)
self.SetSize((300, 200))
self.Centre()
# the create_grid_view() below DOES work when uncommented
#self.create_grid_view(10, 10)
def create_grid_view(self, row_count, col_count):
print("Creating grid view!")
# set up grid panel
panel = wx.Panel(self)
self.main_grid = gridlib.Grid(panel)
self.main_grid.CreateGrid(row_count, col_count)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.main_grid, 1, wx.EXPAND)
panel.SetSizer(sizer)
def open_dialog(self, data):
# data is being used for populating wildcard, etc
with wx.FileDialog(self, "Open a file!",
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
return
file_path = fileDialog.GetPath()
try:
# here, I do some fun data "things" with the file_path
# open it, use other functions, etc.
# afterwards, I need to create a grid
self.create_grid_view(10, 10)
# ^^ This creates a small square instead of a grid.
except IOError:
wx.LogError("Cannot open file '%s'." % file_path)
if __name__ == "__main__":
app = wx.App()
frame = MainFrame(None, title='Window Title :)')
frame.Show()
app.MainLoop()
Expectation:
Actual Results:
Summary:
Why does the create_grid_view() function display a proper grid when called from the init_ux() function, but not the open_dialog() function?
Thanks in advance!
Where you have panel.SetSizer(sizer) either use:
panel.SetSizerAndFit(sizer)
or use:
panel.SetSizer(sizer)
panel.Fit()

wxpython - Erase background erases non-background components

In wxpython, I want to have a window with a picture that changes based on use of toolbar buttons with text controls on top of the picture. When I click the toolbar buttons, I am posting an erase background event, then capturing the erase event, and redrawing the new background from there (base on this).
Mostly works well, except that the text controls cease to be drawn once I redraw the background. They're still there, just not drawn.
Here is a simplified code that demonstrates the problem. If you run this code and click the button to toggle drawing the background image or not, the text controls disappear.:
import wx
import wx.lib.inspection
class PanelWithDrawing(wx.Panel):
def __init__(self, parent):
super(PanelWithDrawing, self).__init__(parent, size=(100, 40))
self.showbmp = False
self.txt = wx.TextCtrl(self, pos=(10, 10))
def onErase(self, dc):
if self.showbmp:
# dc.DrawBitmap(wx.Bitmap('background.png', 0, 0)
dc.DrawRectangle(0, 0, 40, 40) # use a drawing instead so you don't have to find a png
class Toolbar(wx.ToolBar):
def __init__(self, parent):
super(Toolbar, self).__init__(parent, -1)
self.AddLabelTool(wx.ID_SAVE, "Record", wx.Bitmap("picture.png", wx.BITMAP_TYPE_ANY), wx.NullBitmap, wx.ITEM_NORMAL, "", "")
class Example(wx.Frame):
def __init__(self, parent, title):
super(Example, self).__init__(parent, title=title)
self.toolbar = Toolbar(self)
self.SetToolBar(self.toolbar)
self.toolbar.Realize()
self.panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
self.panel1 = PanelWithDrawing(self.panel)
vbox.Add(self.panel1)
# self.panel2 = PanelWithText(self.panel)
# vbox.Add(self.panel2)
self.panel.SetSizer(vbox)
self.Centre()
self.Show()
self.toolbar.Bind(wx.EVT_TOOL, self.onButton)
self.panel1.Bind(wx.EVT_ERASE_BACKGROUND, self.onErase)
def onErase(self, evt):
try:
dc = evt.GetDC()
except:
dc = wx.ClientDC(self)
rect = self.GetUpdateRegion().GetBox()
dc.SetClippingRect(rect)
dc.Clear()
self.panel1.onErase(dc)
def onButton(self, evt):
self.panel1.showbmp = not self.panel1.showbmp
wx.PostEvent(self.panel1, wx.PyCommandEvent(wx.wxEVT_ERASE_BACKGROUND))
if __name__ == '__main__':
app = wx.App()
Example(None, title='Example')
wx.lib.inspection.InspectionTool().Show() # use this for debugging GUI design
app.MainLoop()
How do I tell wxpython to draw all the non-background stuff again? Alternatively, how do I not un-draw it in the first place?
After working on it for a few days, I got it! And the answer is trivially simple (as usual).
wx.PostEvent(self.panel1, wx.PyCommandEvent(wx.wxEVT_ERASE_BACKGROUND)) should be replaced with self.Refresh() to refresh the whole frame and not just force a specific (and apparently unsafe) redraw.

image won't show even after image.Show()

Code below.
If you execute the program as is (just change "moresco.jpg" to any image on your computer), it will first show a black square, and if you click on the search button the image you hardcoded (moresco.jpg in my case) will be displayed.
What I want is to hide the black square at startup and show moresco.jpg when I click on search. So I thought of putting a .Show() over there.
If you uncomment line 22, the black square doesn't show (which is what we want), but then when you click on search moresco.jpg doesn't show.
If you have any suggestions on how to fix this code I would be grateful !
import wx
class gui(wx.Panel):
def __init__(self,parent):
self.parent=parent
wx.Panel.__init__(self,parent)
vsizer = wx.BoxSizer(wx.VERTICAL)
hsizer1 = wx.BoxSizer(wx.HORIZONTAL)
button = wx.Button(self,-1,"search")
self.Bind( wx.EVT_BUTTON,self.display,button)
hsizer1.Add(button,.1,wx.EXPAND)
vsizer.Add(hsizer1,.1,wx.EXPAND)
hsizer2 = wx.BoxSizer(wx.HORIZONTAL)
vsizer.Add(hsizer2,1,wx.EXPAND)
self.pnl=wx.Panel(self)
img = wx.EmptyImage(500,500)
self.imageCtrl = wx.StaticBitmap(self.pnl, wx.ID_ANY,
wx.BitmapFromImage(img))
# uncomment this line and the image won't show even after
# click on search button
#-----------------------------
# print self.imageCtrl.Hide()
#-----------------------------
hsizer3 = wx.BoxSizer(wx.HORIZONTAL)
hsizer3.Add(self.pnl,2,wx.ALIGN_BOTTOM|wx.ALIGN_CENTER_HORIZONTAL,wx.EXPAND)
vsizer.Add(hsizer3,2,wx.EXPAND)
self.SetSizer(vsizer)
self.pnl.Layout()
def display(self,strip):
self.Refresh()
self.Update()
self.imageCtrl.Refresh()
self.imageCtrl.Update()
print self.imageCtrl.Show()
self.imageCtrl.Refresh()
self.imageCtrl.Update()
self.Refresh()
self.Update()
imageFile = "moresco.jpg"
jpg1 = wx.Image(imageFile, wx.BITMAP_TYPE_ANY)
# bitmap upper left corner is in the position tuple (x, y) = (5, 5)
self.imageCtrl.SetBitmap(wx.BitmapFromImage(jpg1))
self.Refresh()
self.Update()
if __name__ == "__main__":
app = wx.App()
w,h=wx.DisplaySize()
frame = wx.Frame(parent=None, id=-1, title="transmorgripy",size=(w/1.2,h/1.2 ))
frame.Center()
panel = gui(frame)
frame.Show()
app.MainLoop()
Using Hide() and Show() on a control does not simply set it to being transparent or not. When it is hidden it does not have a place in its parent panel's Sizer. After you show the image control, it needs a chance to be fit into the parent panel. Depending on exactly how you want it to be displayed you may want to call Fit or Layout.
To show the image and trigger the Sizer giving it a position you could do something like this:
def display(self, strip):
print self.imageCtrl.Show()
imageFile = "moresco.jpg"
jpg1 = wx.Image(imageFile, wx.BITMAP_TYPE_ANY)
# bitmap upper left corner is in the position tuple (x, y) = (5, 5)
self.imageCtrl.SetBitmap(wx.BitmapFromImage(jpg1))
self.Layout()

Inserting Image ruins the frame layout

I am starting using wx for my Python application and I run in a small problem when trying to include an image in a Frame.
I compiled this into a simple example.
What I dont understand is that I have no problem of layout when replacing my image by text. Whe I try entering the image, the layout gets ruined.
Here is my layout with text, that contains only two elements. The logo, and the title of the application
And here is what I got when trying to insert the logo :
For some reason, the layout is ruined, and the title is placed on top left corner (and we can't see it anymore.)
Here is my code :
#!/usr/bin/env python
import wx
class IvolutionWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(200, 100))
self.panel = wx.Panel(self)
# Creating the title layout
title = self.setup_titlelayout()
# Creating the main grid
maingrid = self.setup_maingrid(title)
self.panel.SetSizer(maingrid)
self.panel.Layout()
self.Show(True)
def setup_titlelayout(self):
hbox = wx.BoxSizer(wx.HORIZONTAL) # used to contain logo part and text part
vbox = wx.BoxSizer(wx.VERTICAL) # used to separate title and one-liner
logobox = wx.BoxSizer(wx.HORIZONTAL)
wx_logo = wx.EmptyBitmap(1, 1) # Create a bitmap container object.
wx_logo.LoadFile("ivolution/data/media/vitruve_50.jpg", wx.BITMAP_TYPE_ANY) # Load it with a file image.
#logo = wx.StaticBitmap(self, 1, wx_logo)
logo = wx.StaticText(self.panel, label="Logo Here") # Change for proper logo
title = wx.StaticText(self.panel, label="Ivolution")
logobox.Add(logo)
vbox.Add(title, flag=wx.RIGHT, border=8)
hbox.Add(logobox, flag=wx.RIGHT, border=8)
hbox.Add(vbox, flag=wx.RIGHT, border=8)
return hbox
def setup_maingrid(self, title):
maingrid = wx.FlexGridSizer(4, 1, vgap=0, hgap=0)
maingrid.Add(title)
return maingrid
def on_exit(self, event):
self.Close(True) # Close the frame.
if __name__ == "__main__":
app = wx.App(False)
frame = IvolutionWindow(None, "Ivolution")
app.MainLoop() # Runs application
It should run on any computer if you change the location of the image to a correct one.
The only line of code changing between the two pictures is here :
#logo = wx.StaticBitmap(self, 1, wx_logo)
logo = wx.StaticText(self.panel, label="Logo Here") # Change for proper logo
I am new with wxPython, so I guess there is something obvious here, but I can't find what.
You should use the same parent for the StaticText and the StaticBitmap if you want the same effect.
Thus use:
logo = wx.StaticBitmap(self.panel, 1, wx_logo)

Creating ScrolledWindow in wxPython

I am trying to make a ScrolledWindow that can scroll over a grid of images, but the scrollbar isn't appearing. wxWidgets documentation says:
The most automatic and newest way [to set the scrollbars in wxScrolledWindow] is to simply let sizers determine the scrolling area. This is now the default when you set an interior sizer into a wxScrolledWindow with wxWindow::SetSizer. The scrolling area will be set to the size requested by the sizer and the scrollbars will be assigned for each orientation according to the need for them and the scrolling increment set by wxScrolledWindow::SetScrollRate
So I try to set the sizer of my ScrolledWindow with a GridSizer but it's not working. The code:
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, id=-1,title="",pos=wx.DefaultPosition,
size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
name="frame"):
wx.Frame.__init__(self,parent,id,title,pos,size,style,name)
self.panel = wx.ScrolledWindow(self,wx.ID_ANY)
menuBar = wx.MenuBar()
menu1 = wx.Menu()
m = menu1.Append(wx.NewId(), "&Blah", "Show Pictures")
menuBar.Append(menu1,"&Blah")
self.Bind(wx.EVT_MENU,self.OnInit,m)
self.SetMenuBar(menuBar)
def OnInit(self, event):
sizer = wx.GridSizer(rows=7,cols=3)
filenames = []
for i in range(20):
filenames.append("img"+str(i)+".png")
for fn in filenames:
img = wx.Image(fn,wx.BITMAP_TYPE_ANY)
sizer.Add(wx.StaticBitmap(self.panel,wx.ID_ANY,wx.BitmapFromImage(img)))
self.panel.SetSizer(sizer)
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(parent=None,title="Frame")
self.frame.Show()
self.SetTopWindow(self.frame)
return True
if __name__ == "__main__":
app = MyApp()
app.MainLoop()
Insert this
self.panel.SetScrollbars(1, 1, 1, 1)
after self.panel = wx.ScrolledWindow(self,wx.ID_ANY)
If you want some info on the SetScrollBars method then look at this wxwidgets documentation page

Categories