Introduction and Issue
I've made a gridsizer to resize my frame itself.
But because of the gridsizer if I use WX_EXPAND flag (to let them have a new height and width when I use self.Layout() to refresh when the app is resized) they don't resize the % of the screen I gave them (I put blank widget to put all my widget where I want).
Example here
What I have tried
I've tried to make a wx.GridBagSizer but I can't understand why it always say that GenericTreeCtrl don't exist (its a must I need this tree) so I'm asking a way to do this with wx.GridSizer.
I want to work with something like that and be able to resize my widget: what I want to be resizable
Question
Can you please tell me whats the correct and optimal way to dynamically resize a widget using a wx.GridSizer?
class mainPanel(wx.Panel):
def __init__(self, parent, pageNum, FrameSize):
self.parent = parent
self.pageNum = pageNum
wx.Panel.__init__(self, parent=parent)
Sizer = wx.GridSizer(6,6,0,0)
self.PathList = []
self.PathSelected = []
self.pastePath = ""
self.SetSizer(Sizer)
#tree
widthA,heightA = FrameSize[0],FrameSize[1]
path = "/media/" + os.getlogin()
self.folder_tree_project = wx.GenericDirCtrl(self, wx.ID_ANY,path, (0,0), wx.Size(widthA*0.3,heightA*0.75),wx.FULL_REPAINT_ON_RESIZE|wx.DIRCTRL_MULTIPLE)
Sizer.Add(self.folder_tree_project,0,wx.LEFT,0)
self.t1 = self.folder_tree_project.GetTreeCtrl()
self.folder_tree_project.ShowHidden(False)
self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelect,id=self.t1.GetId())
self.Bind(wx.EVT_SIZE, self.OnResize)
#--------------------------------------------------------
def OnResize(self,event):
FrameSize = self.GetSize()
self.Sizer.Layout()
Try this
def OnResize(self,event):
FrameSize = self.GetSize()
widthA,heightA = FrameSize[0],FrameSize[1]
self.folder_tree_project.SetSize((int(widthA*0.3),int(heightA*0.75)))
self.Sizer.Layout()
I will not be able to test it Leonardo but thanks for the reply ! (i did not answered because of the Global Game Jam that i was participating)
I've found another way thanks to someone on another forum, using the app 'WxGlade' I was able to understand how GridBagSizer work.
You can't do things like GenericDirCtrl in WxGlade but I've read the code given by it by putting button and blank text like I want and putting gridbagsizer, with the previsualization and the generated code I was able to understand how GridBagSizer work.
Related
I'm trying to use the ThumbnailCtrl and I would like to change the background color.
I would have expected this code to work
self.thumbnail_ctrl.SetBackgroundColour('red')
Unfortunately it does not works and I ended hacking a solution show in the following minimal example in which I used the internal field _scrolled setting the background color on it.
I'm still a beginner and would like to ask if I did something wrong with the expected call show above.
#!/usr/bin/env python
import wx
import wx.lib.agw.thumbnailctrl as TC
class MyPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent=parent)
self.thumbnail_ctrl = TC.ThumbnailCtrl(parent=self, imagehandler=TC.NativeImageHandler)
self.thumbnail_ctrl.ShowFileNames(False) # do not show the filename under the thumbs
self.thumbnail_ctrl.SetThumbOutline(TC.THUMB_OUTLINE_RECT) # outline the rect
self.main_sizer = wx.BoxSizer(wx.VERTICAL)
self.main_sizer.Add(window=self.thumbnail_ctrl, proportion=1, flag=wx.ALL|wx.EXPAND, border=0)
self.SetSizer(self.main_sizer)
# Hack to set the background color of self.thumbnail_ctrl
# self.thumbnail_ctrl.SetBackgroundColour('red') # Calling this does not works
self.thumbnail_ctrl._scrolled.SetBackgroundColour('red') # Calling this the background color is set to red
self.Refresh()
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title='minimal set background example')
self.panel = MyPanel(parent=self)
self.Show()
if __name__ == '__main__':
app = wx.App(redirect=True)
frame = MyFrame()
app.MainLoop()
As far as I remember I haven’t put in any way to set the background color of Thumbnailctrl - I simply forgot I guess. So your solution, while of course a hack, it’s a perfectly valid one. You may want to open an issue or submit a PR to wxPython github so a proper SetBackgroundColour can be added to the class in the source.
Solved:
Thanks to Aya's answer below I now know that the issue was caused by self.panel = wx.Panel(self, -1) on line 18. I created a panel and didn't attach anything to it. The original issue description is still below for reference.
My Google-fu has failed me. I'm building the text editor that you can find here, written in Python with wxPython:
https://github.com/joshsaintjacque/py-ed/blob/master/pyed.py
The issue that I'm running into is this: when I open a text file (the only functionality built in at this point) that's larger than the viewable area in the TextCtrl the scroll bar remains disabled until the window is re-sized, then it works fine.
I know that the act of re-sizing the window is running some command that I'm neglecting to include in my OpenFile function (or perhaps in init), but I can't figure out what.
Any thoughts anyone has that could lead me in the right direction would be greatly appreciated.
Thanks!
+1 for including a link to the full source code - makes it so much easier to test.
I couldn't reproduce the fault you describe on wxPython 2.8.12 on Win32, but upon running your code, I found a seemingly extraneous wx.Panel object being created on pyed.py line 18...
self.panel = wx.Panel(self, -1)
...which seems to be interfering with the correct operation of the program. After commenting out that line, it seems to work fine.
A couple of other things I noticed: line 56...
self.SetTitle("PyEd - Editing ... " + filename)
...should probably be put in the preceding if-block, otherwise you'll get an error if the user clicks "Cancel" on the wx.FileDialog, and on line 16...
wx.Frame.__init__(self, parent, id, 'PyEd', (-1, -1), wx.Size(640, 480))
...if you use keyword args rather than positional args...
wx.Frame.__init__(self, parent=parent, id=id, title='PyEd', size=wx.Size(640, 480))
...you needn't bother re-specifying the default value for the window position, which is also slightly safer, in case the wxPython developers decide to change the defaults in a future version.
You can also factor out constant values, and the optional creation of the wx.Size object to reduce that line to...
wx.Frame.__init__(self, parent=None, title='PyEd', size=(640, 480))
Finally, with regards to IDs: in most cases you'll probably find they're of little use. Where they come in handy is where you want many similar controls, and it makes more sense to have them handled by a single event handler function.
Consider this example...
def create_buttons(parent):
parent.button1 = wx.Button(label='Button 1')
parent.button2 = wx.Button(label='Button 2')
parent.button3 = wx.Button(label='Button 3')
parent.button1.Bind(wx.EVT_BUTTON, on_button_1)
parent.button2.Bind(wx.EVT_BUTTON, on_button_2)
parent.button3.Bind(wx.EVT_BUTTON, on_button_3)
def on_button_1(event):
print 'You clicked button 1'
def on_button_2(event):
print 'You clicked button 2'
def on_button_3(event):
print 'You clicked button 3'
...which is fine, but if you need, say, 100 buttons, you may prefer to implement it like this...
def create_buttons(parent):
parent.buttons = [wx.Button(id=i, label='Button %d' % i) for i in range(100)]
parent.Bind(wx.EVT_BUTTON, on_button)
def on_button(event):
button_id = event.GetId()
print 'You clicked button %d' % button_id
Oh, and be careful using id as a variable name, because it's also a Python built-in function name.
It looks as if you're not setting the min or max size hints for the window, nor are you calling Self.Fit() to fit the box sizer to the window size (or is it the other way round? I'm rusty on my wxPython...)
Right where you call self.SetSizer(sizer), you should be able to fix this by adding:
self.Fit()
self.SetSizeHintSz(minSize=wx.Size(640, 480))
You may be able to get around the separate call to self.Fit() by using self.SetSizerAndFit()
(edited for spelling.)
I've been having some problems with a program that I've been writing and would appreciate some help or input. For some background, I'm using Python 2.7 and wxPython in order to do a streaming webcam client. The client gets the images from the server in its own thread, and puts them into a Queue. The GUI thread then gets those images from the Queue and converts them to a wxBitmap object. This happens every .5 seconds and works just great. I am able to save the wxBitmap object as a file so I know that everything is working properly.
The problem that I'm having is actually getting the wxBitmap object to show up on my GUI. The only thing I seem to be able to make the GUI do is display a gray rectangle where the web cam image should be.
Here is my onPaint() that is called when I want to refresh the screen:
def onPaint(self,e):
## this is the function that actually draws and redraws the window
## to be displayed. I think it is something similar to blit()
## in other graphical display frameworks
print "in onPaint"
## create the device context object (graphics painter)
dc = wx.PaintDC(self)
dc.BeginDrawing()
## draw the bitmap to the screen
dc.DrawBitmap(self.imageBit,0,0,True)
dc.EndDrawing()
## test code.
## the following works and updates, which means that
## everything is being converted properly and updated.
## not sure why the dc won't paint it to the window.
self.imageBit.SaveFile("bit.bmp", wx.BITMAP_TYPE_BMP)
Simply put, I'm at a loss as to why its not working. from my research I've found that because I'm on a windows machine I needed the BeginDrawing() and EndDrawing() functions, so I added them. Still doesn't work. There are no errors or exceptions being thrown.
other questions that might help solve this issue:
I'm updating a wxFrame object. Maybe the wxPaintDC needs to operate in another type of container to work?
?
Actually, maybe my __init__ function is whats holding the problem. Am I setting this up properly?
class viewWindow(wx.Frame):
imgSizer = (480,360)
def __init__(self, *args, **kw):
## this is called when an instance of this class is created
super(viewWindow,self).__init__(*args,**kw)
## here is where the actual stuff inside the frame is set up.
self.pnl = wx.Panel(self)
## create a button that opens up a Connection Window
#test = wx.Button(self.pnl, label='Connection Settings')
## test.Bind(wx.EVT_BUTTON, self.openConnectionWindow)
## create the wxImage for the web cam pic
self.image = wx.EmptyImage(self.imgSizer[0],self.imgSizer[1])
## create the wxBitmap so that the wxImage can be displayed
self.imageBit = wx.BitmapFromImage(self.image)
## create a timer that will update the window based of frame rate
self.timex = wx.Timer(self, wx.ID_OK)
self.timex.Start(500)
self.Bind(wx.EVT_TIMER, self.redraw, self.timex)
## need to do the following in order to display images in wxPython:
self.Bind(wx.EVT_PAINT, self.onPaint)
self.SetSize(self.imgSizer)
self.SetTitle('View Window')
self.Show()
Anyways, thanks in advance for your help.
EDIT: I solved the problem accidentally by deleting the line self.pnl = wx.Panel(self).
So apparently it was rendering properly, but the bitmap was underneath the panel. Maybe? I'm not really sure. I'm new to this whole wxPython thing.
That appears to be what the wxPython demo is doing too: dc.DrawBitmap. And it works on Windows! At least, that's what they do in the AlphaDrawing demo. In the DrawImage demo, they use dc.Blit(). You might try that.
However, I wonder if you couldn't do it like I did with my photo viewer. I don't use DCs to draw, but instead just use a wx.StaticBitmap that I update.
This code works. It displays the images every time and all that. It does tend to 'flicker', though. So there is probably a better way of doing this that I'm not aware of.
class viewWindow(wx.Frame):
imgSizer = (480,360)
def __init__(self, parent, title="View Window"):
super(viewWindow,self).__init__(parent)
## create the menu and its sub trees
menubar = wx.MenuBar()
filemenu = wx.Menu()
menubar.Append(filemenu, 'File')
self.fitem = filemenu.Append(wx.ID_ANY, 'Open Connection Window')
self.Bind(wx.EVT_MENU, self.openConnectionWindow, self.fitem)
self.SetMenuBar(menubar)
## here is where the actual stuff inside the frame is set up.
self.pnl = wx.Panel(self)
self.vbox = wx.BoxSizer(wx.VERTICAL)
## create the wxImage for the web cam pic
self.image = wx.EmptyImage(self.imgSizer[0],self.imgSizer[1])
## create the wxBitmap so that the wxImage can be displayed
self.imageBit = wx.BitmapFromImage(self.image)
self.staticBit = wx.StaticBitmap(self.pnl,wx.ID_ANY, self.imageBit)
## add the staticBit to the sizer so it is rendered properly on resizes and such
## note: not actually needed to get the image to display, but reccommended for ease
## of layout
self.vbox.Add(self.staticBit)
## register the sizer with the panel so the panel knows to use it.
self.pnl.SetSizer(self.vbox)
## create a timer that will update the window based on frame rate
self.timex = wx.Timer(self, wx.ID_OK)
self.timex.Start(1000/framerate)
self.Bind(wx.EVT_TIMER, self.redraw, self.timex)
## set the size of the frame itself when it is first opened
self.SetSize(self.imgSizer)
self.Show()
def openConnectionWindow(self, e):
## this will open a new connection window
connect = connectionWindow(None)
def redraw(self,e):
## this function updates the frame with the latest web cam image that has been
## retrieved by the client thread from the server.
## get the newest image in the queue
if not imgQ.empty():
picz = imgQ.get()
## convert the image from a string to something usable (wxImage)
self.image.SetData(picz)
## from wxImage to wxBitmap
self.imageBit = wx.BitmapFromImage(self.image)
self.staticBit = wx.StaticBitmap(self.pnl,wx.ID_ANY, self.imageBit)
## refresh the frame
self.Refresh()
Several hours later, and researching a different question I had, I find this:
How to detect motion between two PIL images? (wxPython webcam integration example included)
That has example code in it that works beautifully.
OK, so iv almost completed my program for my project but I cant get a BUTTON_EVT to work which if i am honest should be the easiest thing todo. I have the buttons on my program which represent the hardware and I have created a def function for them to appear on the OGL canvas.
Problem has been solved... The code associated with the problem is found in the answer below
Edited from your last comment. Use this (using your own images):
def OnClickRouter(self, event):
image=wx.Image('cat.jpg', wx.BITMAP_TYPE_JPEG)
self.frame = bucky(None, image)
self.frame.Show()
If you call bucky() this way you must also fix the class signature:
class bucky(wx.Frame):
# Creating the outer window/frame
def __init__(self, parent, image=None):
wx.Frame.__init__(self, parent, -1,'Karls Network Tool', size=(900,700))
my_image = image if image else wx.Image("myself.bmp", wx.BITMAP_TYPE_BMP)
''''''''''''''''''''''''''''''''
# Button images
buttonOneRouter = my_image.ConvertToBitmap()
self.buttonOneRouter = wx.BitmapButton(panel, -1, buttonOneRouter, pos=(20,340))
self.buttonOneRouter.Bind(wx.EVT_BUTTON, self.OnClickRouter)
''''''''''''''''''''''''''''''''
Then you can see that after clicking the buttonOnerouter what actually you are doing is opening a new frame. The left figure is what I get when I run the program, the right one is after I click and enter again my name (I simplified a bit your code. Thats why you only see one button at the bottom instead of 4):
If you want to put my cat in the canvas instead of in the button there is still some work to do. I recommend to you to give a look at the wxPython demo. In the miscellaneous group of examples you have one called OGL that shows how to do that.
Edit: You can download the wxPython docs and demos package from here
I don't know if this is right or not but I suggest you to take this approach and see if it works or not.
Modify your frame class as:
def __init(self,parent,id,img=None)
def onClickRouter(self,event):
image=wx.Image('router.jpg', wx.BITMAP_TYPE_JPEG)
temp = image.ConvertToBitmap()
self.bmp = wx.StaticBitmap(parent=self, bitmap=temp)
self.frame=bucky(self.bmp)
Please let know the outcome.
I am not sure what they are called, but I would like a resizable divider line, to separate widgets.
I would like something like this (except horizontal):
http://imm.io/bKgf
If you do not know what i am talking about please comment, thanks and sorry for my ignorance.
You need, maybe, a splitterwindow:
import wx
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.splitter = wx.SplitterWindow(self)
pan1 = wx.Window(self.splitter, style=wx.BORDER_SUNKEN)
pan1.SetBackgroundColour("yellow")
wx.StaticText(pan1, -1, "My Left Panel")
pan2 = wx.Window(self.splitter, style=wx.BORDER_SUNKEN)
pan2.SetBackgroundColour("orange")
wx.StaticText(pan2, -1, "my Right Panel")
self.splitter.SplitVertically(pan1, pan2, -100)
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = MyFrame(None)
frame.Show()
app.MainLoop()
I'm not sure if this will exactly meet your needs, but you might have a look at wxPython's aui module. There's some demo code on that page that'll get you started, and the wxPython demo has some good examples to try out.
What you want is wx.StaticLine. Just create one of those and add it to your sizer with the EXPAND style flag. See http://www.wxpython.org/docs/api/wx.StaticLine-class.html for more info.
EDIT: Oh, I think Joaquin's suggestion for a SplitterWindow may be spot on. Note that there a couple other types of SplitterWindows, like MultiSplitterWindow and FourWaySplitter