wxpython drag&drop focus problem - python

I'd like to implement drag&drop in wxPython that works in similar way that in WordPad/Eclipse etc. I mean the following:
when something is being dropped to WordPad, WordPad window is on top with focus and text is added. In Eclipse editor text is pasted, Eclipse window gains focus and is on top.
When I implement drag&drop using wxPython target window is not brought to front. I implemented drag&drop in similar way to (drag):
import wx
class DragFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.tree = wx.TreeCtrl(self, wx.ID_ANY)
root = self.tree.AddRoot("root item")
self.tree.AppendItem(root, "child 1")
self.tree.Bind(wx.EVT_TREE_BEGIN_DRAG, self.__onBeginDrag)
def __onBeginDrag(self, event):
tdo = wx.PyTextDataObject(self.tree.GetItemText(event.GetItem()))
dropSource = wx.DropSource(self.tree)
dropSource.SetData(tdo)
dropSource.DoDragDrop(True)
app = wx.PySimpleApp()
frame = DragFrame()
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
Second program (drop):
import wx
class TextDropTarget(wx.TextDropTarget):
def __init__(self, obj):
wx.TextDropTarget.__init__(self)
self.obj = obj
def OnDropText(self, x, y, data):
self.obj.WriteText(data + '\n\n')
wx.MessageBox("Error", "Error", style = wx.ICON_ERROR)
class DropFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
text = wx.TextCtrl(self, wx.ID_ANY)
text.SetDropTarget(TextDropTarget(text))
app = wx.PySimpleApp()
frame = DropFrame()
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
When you run both programs, place windows in the centre of the screen (part of drop window is visible), then drag a node from drag window to drop window - target window displays message box which isn't visible, target window is hidden behind source window.
How to implement drag&drop that will focus on the second (target) window? I've tried adding window.Show(), window.SetFocus(), even using some functions of WinAPI (through win32gui). I think there should be some standard way of doing this. What am I missing?

You need to do anything you want int DragOver method of DropTarget e.g. there you can raise and set focus on your window
sample working code for target
import wx
class TextDropTarget(wx.TextDropTarget):
def __init__(self, obj, callback):
wx.TextDropTarget.__init__(self)
self.obj = obj
self._callback = callback
def OnDropText(self, x, y, data):
self.obj.WriteText(data + '\n\n')
wx.MessageBox("Error", "Error", style = wx.ICON_ERROR)
def OnDragOver(self, *args):
wx.CallAfter(self._callback)
return wx.TextDropTarget.OnDragOver(self, *args)
class DropFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
text = wx.TextCtrl(self, wx.ID_ANY)
text.SetDropTarget(TextDropTarget(text, self._callback))
def _callback(self):
self.Raise()
self.SetFocus()
app = wx.PySimpleApp()
frame = DropFrame()
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()

Wouldn't this work?
class DropFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
text = wx.TextCtrl(self, wx.ID_ANY)
self.SetFocus() # Set's the focus to this window, allowing it to receive keyboard input.
text.SetDropTarget(TextDropTarget(text))
wx.Frame inherits from wx.Window, that has SetFocus(self).
I have just tested it and it works. Just moved SetFocus before SetDropTarget, as its a cleaner behavior.

Related

My wx.OnPaint() function is not called constantly in the wx main loop

I working on a project using OpenGL and wxPython to make a navigatable 3D user interface.
However, the OnPaint() function doesn't seem to be called continuously in the main loop. That makes my interface not being updated constantly. The function is only called I drag around the window. For example: when I press an arrow key, the object in the window only moves when I drag around the window.
I boiled my code down to these lines of codes. Can anyone help me make the OnPaint() function called constantly in print a bunch of "HI" without the need to drag around the window?
if __name__ == "__main__":
app = MyApp()
app.MainLoop()
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame()
frame.Show()
return True
class MyFrame(wx.Frame):
def __init__(self):
self.size = (1000, 700)
wx.Frame.__init__(self, None, title ="wx", size=self.size)
self.canvas = MyCanvas(self)
class MyCanvas(GLCanvas):
def __init__(self, parent):
GLCanvas.__init__(self, parent, -1, size=(1000, 700))
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, event):
print("HI")
A wx.Window like wx.Frame or GLCanvascan be forced to be repainted by .Refresh().
e.g. You can use the wx.IdleEvent to triggers the canvas to be refreshed continuously.
class MyFrame(wx.Frame):
def __init__(self):
self.size = (1000, 700)
wx.Frame.__init__(self, None, title ="wx", size=self.size)
self.canvas = MyCanvas(self)
self.Bind(wx.EVT_IDLE, self.OnIdle)
def OnIdle(self, event):
self.Refresh() # refresh self and all its children
#self.canvas.Refresh() # refresh self.canvas

How to add OSX menu bar icon with wxPython

I would like to add an icon to the OSX menu bar at the top of the screen using wxPython. I have tried wx.TaskBarIcon, which adds a System Tray icon in Windows, but this doesn't work - it changes the Dock icon for the app instead. Does anyone know how to do this?
It seems that with wxPython2.9-osx-cocoa-py2.7 you can in fact put up a menubar icon. It looks like you can also call PopupMenu() on TaskBarIcon to attach a menu, which you should be able to use to create a full blown OSX menu bar application.
import wx
class TaskBarFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, style=wx.FRAME_NO_TASKBAR |
wx.NO_FULL_REPAINT_ON_RESIZE)
self.tbicon = wx.TaskBarIcon()
icon = wx.Icon('myicon.ico', wx.BITMAP_TYPE_ICO)
self.tbicon.SetIcon(icon, '')
app = wx.App(False)
frame = TaskBarFrame(None)
frame.Show(False)
app.MainLoop()
Answer provided here on Google Groups - in summary, you can't do it.
It seems now you can:
http://wiki.wxpython.org/Custom%20Mac%20OsX%20Dock%20Bar%20Icon?highlight=%28wx%5C.TaskBarIcon%29
just copy and paste the code and run it ;)
#!/usr/bin/env pythonw
import wx
import wx.lib.embeddedimage
WXPdemo = wx.lib.embeddedimage.PyEmbeddedImage(
"iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAWlJ"
"REFUWIW1V1sSwjAIBMebeBU9db2KZ8EPmxbCI4TUnXGskWaXDQktwhjErjERP4XRhER08iPi"
"5SKiyQR5JyI7xxB3j7wn5GI6V2hFxM0gJtjYANFBiIjQu7L/1lYlwR0QxLDZhE0II1+CtwRC"
"RI8riBva7DL7CC9VAwDbbxwKtdDXwBi7K+1zCP99T1vDFedd8FBwYd6BCAUXuACEF7QsbET/"
"FaHs+gDQw4vOLNHkMojAnTw8nlNipIiwmR0DCXJbjCXkFCAL23BnpQgRWt1EMbyujCK9AZzZ"
"f+b3sX0oSqJQ6EorFeT4NiL6Wtj0+LXnQAzThYoAAsN6ehqR3sHExmcEqGeFApQLcTvm5Kt9"
"wkHGgb+RZwSkyc1dwOcpCtCoNKSz6FRCUQ3o7Nn+5Y+Lg+y5CIXlcyAk99ziiQS32+svz/UY"
"vClJoLpIC8gi+VwwfDecEiEtT/WZTJDf94uk1Ru8vbz0cvoF7S2DnpeVL9UAAAAASUVORK5C"
"YII=")
class DemoTaskBarIcon(wx.TaskBarIcon):
TBMENU_RESTORE = wx.NewId()
TBMENU_CLOSE = wx.NewId()
TBMENU_CHANGE = wx.NewId()
TBMENU_REMOVE = wx.NewId()
def __init__(self, frame):
wx.TaskBarIcon.__init__(self)
self.frame = frame
# Set the image
icon = self.MakeIcon(WXPdemo.GetImage())
self.SetIcon(icon, "wxPython Demo")
self.imgidx = 1
# bind some events
self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
def CreatePopupMenu(self):
"""
This method is called by the base class when it needs to popup
the menu for the default EVT_RIGHT_DOWN event. Just create
the menu how you want it and return it from this function,
the base class takes care of the rest.
"""
menu = wx.Menu()
menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
menu.Append(self.TBMENU_CLOSE, "Close wxPython Demo")
return menu
def MakeIcon(self, img):
"""
The various platforms have different requirements for the
icon size...
"""
if "wxMSW" in wx.PlatformInfo:
img = img.Scale(16, 16)
elif "wxGTK" in wx.PlatformInfo:
img = img.Scale(22, 22)
# wxMac can be any size upto 128x128, so leave the source img alone....
icon = wx.IconFromBitmap(img.ConvertToBitmap() )
return icon
def OnTaskBarActivate(self, evt):
if self.frame.IsIconized():
self.frame.Iconize(False)
if not self.frame.IsShown():
self.frame.Show(True)
self.frame.Raise()
def OnTaskBarClose(self, evt):
wx.CallAfter(self.frame.Close)
class MainFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="Hello World")
self.tbicon = DemoTaskBarIcon(self)
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
def OnCloseWindow(self, evt):
self.tbicon.Destroy()
evt.Skip()
app = wx.App(redirect=False)
frame = MainFrame(None)
frame.Show(True)
app.MainLoop()

Return value from wxPython Frame

Could someone show me how I could return a value from a wxPython Frame? When the use clicks close, I popup a message dialog asking him a question. I would like to return the return code of this message dialog to my calling function.
Thanks
Because the wxFrame has events that process via the app.MainLoop() functionality, the only way to get at the return value of a wx.Frame() is via catching an event.
The standard practice of handling events is typically from within the class which derives from wx.Window itself (e.g., Frame, Panel, etc.). Since you want code exterior to the wx.Frame to receive information that was gathered upon processing the OnClose() event, then the best way to do that is to register an event handler for your frame.
The documentation for wx.Window::PushEventHandler is probably the best resource and even the wxpython wiki has a great article on how to do this. Within the article, they register a custom handler which is an instance of "MouseDownTracker." Rather than instantiating within the PushEventHandler call, you'd want to instantiate it prior to the call so that you can retain a handle to the EventHandler derived class. That way, you can check on your derived EventHandler class-variables after the Frame has been destroyed, or even allow that derived class to do special things for you.
Here is an adaptation of that code from the wx python wiki (admittedly a little convoluted due to the requirement of handling the results of a custom event with a "calling" function):
import sys
import wx
import wx.lib.newevent
(MyCustomEvent, EVT_CUSTOM) = wx.lib.newevent.NewEvent()
class CustomEventTracker(wx.EvtHandler):
def __init__(self, log, processingCodeFunctionHandle):
wx.EvtHandler.__init__(self)
self.processingCodeFunctionHandle = processingCodeFunctionHandle
self.log = log
EVT_CUSTOM(self, self.MyCustomEventHandler)
def MyCustomEventHandler(self, evt):
self.log.write(evt.resultOfDialog + '\n')
self.processingCodeFunctionHandle(evt.resultOfDialog)
evt.Skip()
class MyPanel2(wx.Panel):
def __init__(self, parent, log):
wx.Panel.__init__(self, parent)
self.log = log
def OnResults(self, resultData):
self.log.write("Result data gathered: %s" % resultData)
class MyFrame(wx.Frame):
def __init__(self, parent, ID=-1, title="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
wx.Frame.__init__(self, parent, ID, title, pos, size, style)
self.panel = panel = wx.Panel(self, -1, style=wx.TAB_TRAVERSAL | wx.CLIP_CHILDREN | wx.FULL_REPAINT_ON_RESIZE)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add((25, 25))
row = wx.BoxSizer(wx.HORIZONTAL)
row.Add((25,1))
m_close = wx.Button(self.panel, wx.ID_CLOSE, "Close")
m_close.Bind(wx.EVT_BUTTON, self.OnClose)
row.Add(m_close, 0, wx.ALL, 10)
sizer.Add(row)
self.panel.SetSizer(sizer)
def OnClose(self, evt):
dlg = wx.MessageDialog(self, "Do you really want to close this frame?", "Confirm Exit", wx.OK | wx.CANCEL | wx.ICON_QUESTION)
result = dlg.ShowModal()
dlg.Destroy()
if result == wx.ID_CANCEL:
event = MyCustomEvent(resultOfDialog="User Clicked CANCEL")
self.GetEventHandler().ProcessEvent(event)
else: # result == wx.ID_OK
event = MyCustomEvent(resultOfDialog="User Clicked OK")
self.GetEventHandler().ProcessEvent(event)
self.Destroy()
app = wx.App(False)
f2 = wx.Frame(None, title="Frame 1 (for feedback)", size=(400, 350))
p2 = MyPanel2(f2, sys.stdout)
f2.Show()
eventTrackerHandle = CustomEventTracker(sys.stdout, p2.OnResults)
f1 = MyFrame(None, title="PushEventHandler Tester (deals with on close event)", size=(400, 350))
f1.PushEventHandler(eventTrackerHandle)
f1.Show()
app.MainLoop()
You can get the result of clicking the OK, CANCEL buttons from the Dialog ShowModal method.
Given dialog is an instance of one of the wxPython Dialog classes:
result = dialog.ShowModal()
if result == wx.ID_OK:
print "OK"
else:
print "Cancel"
dialog.Destroy()
A few years late for the initial question, but when looking for the answer to this question myself, I stumbled upon a built-in method of getting a return value from a modal without messing with any custom event funniness. Figured I'd post here in case anyone else needs it.
It's simply this guy right here:
wxDialog::EndModal void EndModal(int retCode)
Ends a modal dialog, passing a value to be returned from the
*wxDialog::ShowModal invocation.*
Using the above, you can return whatever you want from the Dialog.
An example usage would be subclassing a wx.Dialog, and then placing the EndModal function in the button handlers.
class ProjectSettingsDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, "Project Settings", size=(600,400))
sizer = wx.BoxSizer(wx.VERTICAL) #main sized
sizer.AddStretchSpacer(1)
msg = wx.StaticText(self, -1, label="This is a sample message")
sizer.Add(msg, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 15)
horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
okButton = wx.Button(self, -1, 'OK')
self.Bind(wx.EVT_BUTTON, self.OnOK, okButton)
cancelBtn = wx.Button(self, -1, "Cancel")
self.Bind(wx.EVT_BUTTON, self.OnCancel, cancelBtn)
horizontal_sizer.Add(okButton, 0, wx.ALIGN_LEFT)
horizontal_sizer.AddStretchSpacer(1)
horizontal_sizer.Add(cancelBtn, 0, wx.ALIGN_RIGHT)
sizer.Add(horizontal_sizer, 0)
sizer.AddStretchSpacer(1)
self.SetSizer(sizer)
def OnOK(self, event):
self.EndModal(wx.ID_OK) #returns numeric code to caller
self.Destroy()
def OnCancel(self, event):
self.EndModal(wx.ID_CANCEL) #returns numeric code to caller
self.Destroy()
(Note: I just banged this code out quickly; didn't test the sizers)
As you can see, all you need to do is call the EndModal from a button event to return a value to whatever spawned the dialog.
I wanted to do the same thing, to have a graphical "picker" that I could run from within a console app. Here's how I did it.
# Fruit.py
import wx
class Picker (wx.App):
def __init__ (self, title, parent=None, size=(400,300)):
wx.App.__init__(self, False)
self.frame = wx.Frame(parent, title=title, size=size)
self.apple_button = wx.Button(self.frame, -1, "Apple", (0,0))
self.apple_button.Bind(wx.EVT_BUTTON, self.apple_button_click)
self.orange_button = wx.Button(self.frame, -1, "Orange", (0,100))
self.orange_button.Bind(wx.EVT_BUTTON, self.orange_button_click)
self.fruit = None
self.frame.Show(True)
def apple_button_click (self, event):
self.fruit = 'apple'
self.frame.Destroy()
def orange_button_click (self, event):
self.fruit = 'orange'
self.frame.Destroy()
def pick (self):
self.MainLoop()
return self.fruit
Then from a console app, I would run this code.
# Usage.py
import Fruit
picker = Fruit.Picker('Pick a Fruit')
fruit = picker.pick()
print 'User picked %s' % fruit
user1594322's answer works but it requires you to put all of your controls in your wx.App, instead of wx.Frame. This will make recycling the code harder.
My solution involves define a "PassBack" variable when defining your init function. (similar to "parent" variable, but it is normally used already when initiating a wx.Frame)
From my code:
class MyApp(wx.App):
def __init__ (self, parent=None, size=(500,700)):
wx.App.__init__(self, False)
self.frame = MyFrame(parent, -1, passBack=self) #Pass this app in
self.outputFromFrame = "" #The output from my frame
def getOutput(self):
self.frame.Show()
self.MainLoop()
return self.outputFromFrame
and for the frame class:
class MyFrame(wx.Frame):
def __init__(self, parent, ID, passBack, title="My Frame"):
wx.Frame.__init__(self, parent, ID, title, size=(500, 700))
self.passBack = passBack #this will be used to pass back variables/objects
and somewhere during the execution of MyFrame
self.passBack.outputFromFrame = "Hello"
so all in all, to get a string from an application
app = MyApp()
val = app.getOutput()
#Proceed to do something with val
Check this answer on comp.lang.python: Linkie
I don't think a wxFrame can return a value since it is not modal. If you don't need to use a wxFrame, then a modal dialog could work for you. If you really need a frame, I'd consider using a custom event.
It would go something like this:
(1) User clicks to close the wxFrame
(2) You override OnClose (or something like that) to pop up a dialog to ask the user a question
(3) Create and post the custom event
(4) Close the wxFrame
(5) Some other code processes your custom event
I think I just had the same problem as you. Instead of making that popup a frame, I made it a dialog instead. I made a custom dialog by inheriting a wx.dialog instead of a wx.frame. Then you can utilize the code that joaquin posted above. You check the return value of the dialog to see what was entered. This can be done by storing the value of the textctrl when the user clicks ok into a local variable. Then before it's destroyed, you get that value somehow.
The custom dialog section of this site helped me out greatly.
http://zetcode.com/wxpython/dialogs/

Scrolling through a `wx.ScrolledPanel` with the mouse wheel and arrow keys

In my wxPython application I've created a wx.ScrolledPanel, in which there is a big wx.StaticBitmap that needs to be scrolled.
The scroll bars do appear and I can scroll with them, but I'd also like to be able to scroll with the mouse wheel and the arrow keys on the keyboard. It would be nice if the "Home", "Page Up", and those other keys would also function as expected.
How do I do this?
UPDATE:
I see the problem. The ScrolledPanel is able to scroll, but only when it is under focus. Problem is, how do I get to be under focus? Even clicking on it doesn't do it. Only if I put a text control inside of it I can focus on it and thus scroll with the wheel. But I don't want to have a text control in it. So how do I make it focus?
UPDATE 2:
Here is a code sample that shows this phenomena. Uncomment to see how a text control makes the mouse wheel work.
import wx, wx.lib.scrolledpanel
class MyFrame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
scrolled_panel = \
wx.lib.scrolledpanel.ScrolledPanel(parent=self, id=-1)
scrolled_panel.SetupScrolling()
text = "Ooga booga\n" * 50
static_text=wx.StaticText(scrolled_panel, -1, text)
sizer=wx.BoxSizer(wx.VERTICAL)
sizer.Add(static_text, wx.EXPAND, 0)
# Uncomment the following 2 lines to see how adding
# a text control to the scrolled panel makes the
# mouse wheel work.
#
#text_control=wx.TextCtrl(scrolled_panel, -1)
#sizer.Add(text_control, wx.EXPAND, 0)
scrolled_panel.SetSizer(sizer)
self.Show()
if __name__=="__main__":
app = wx.PySimpleApp()
my_frame=MyFrame(None, -1)
#import cProfile; cProfile.run("app.MainLoop()")
app.MainLoop()
Problem is on window Frame gets the focus and child panel is not getting the Focus (on ubuntu linux it is working fine). Workaround can be as simple as to redirect Frame focus event to set focus to panel e.g.
import wx, wx.lib.scrolledpanel
class MyFrame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = scrolled_panel = \
wx.lib.scrolledpanel.ScrolledPanel(parent=self, id=-1)
scrolled_panel.SetupScrolling()
text = "Ooga booga\n" * 50
static_text=wx.StaticText(scrolled_panel, -1, text)
sizer=wx.BoxSizer(wx.VERTICAL)
sizer.Add(static_text, wx.EXPAND, 0)
scrolled_panel.SetSizer(sizer)
self.Show()
self.panel.SetFocus()
scrolled_panel.Bind(wx.EVT_SET_FOCUS, self.onFocus)
def onFocus(self, event):
self.panel.SetFocus()
if __name__=="__main__":
app = wx.PySimpleApp()
my_frame=MyFrame(None, -1)
app.MainLoop()
or onmouse move over panel, set focus to it, and all keys + mousewheeel will start working e.g.
import wx, wx.lib.scrolledpanel
class MyFrame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = scrolled_panel = \
wx.lib.scrolledpanel.ScrolledPanel(parent=self, id=-1)
scrolled_panel.SetupScrolling()
scrolled_panel.Bind(wx.EVT_MOTION, self.onMouseMove)
text = "Ooga booga\n" * 50
static_text=wx.StaticText(scrolled_panel, -1, text)
sizer=wx.BoxSizer(wx.VERTICAL)
sizer.Add(static_text, wx.EXPAND, 0)
scrolled_panel.SetSizer(sizer)
self.Show()
def onMouseMove(self, event):
self.panel.SetFocus()
if __name__=="__main__":
app = wx.PySimpleApp()
my_frame=MyFrame(None, -1)
app.MainLoop()
Here's an example that should do what you want, I hope. (Edit: In retrospect, this doesnt' quite work, for example, when there are two scrolled panels... I'll leave it up here though so peole can downvote it or whatever.) Basically I put everything in a panel inside the frame (generally a good idea), and then set the focus to this main panel.
import wx
import wx, wx.lib.scrolledpanel
class MyFrame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
main_panel = wx.Panel(self, -1)
main_panel.SetBackgroundColour((150, 100, 100))
self.main_panel = main_panel
scrolled_panel = \
wx.lib.scrolledpanel.ScrolledPanel(parent=main_panel, id=-1)
scrolled_panel.SetupScrolling()
self.scrolled_panel = scrolled_panel
cpanel = wx.Panel(main_panel, -1)
cpanel.SetBackgroundColour((100, 150, 100))
b = wx.Button(cpanel, -1, size=(40,40))
self.Bind(wx.EVT_BUTTON, self.OnClick, b)
self.b = b
text = "Ooga booga\n" * 50
static_text=wx.StaticText(scrolled_panel, -1, text)
main_sizer=wx.BoxSizer(wx.VERTICAL)
main_sizer.Add(scrolled_panel, 1, wx.EXPAND)
main_sizer.Add(cpanel, 1, wx.EXPAND)
main_panel.SetSizer(main_sizer)
text_sizer=wx.BoxSizer(wx.VERTICAL)
text_sizer.Add(static_text, 1, wx.EXPAND)
scrolled_panel.SetSizer(text_sizer)
self.main_panel.SetFocus()
self.Show()
def OnClick(self, evt):
print "click"
if __name__=="__main__":
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1)
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()
For keyboard control, like setting action from the home key, I think you'll need to bind to those events, and respond appropriately, such as using mypanel.Scroll(0,0) for the home key (and remember to call evt.Skip() for the keyboard events you don't act on). (Edit: I don't think there are any default key bindings for scrolling. I'm not sure I'd want any either, for example, what should happen if there's a scrolled panel within a scrolled panel?)

wxPython: Good way to overlay a wx.Panel on an existing wx.Panel

I have a wx.Frame, in which there is a main wx.Panel with several widgets inside of it. I want one button in there to cause a "help panel" to come up. This help panel would probably be a wx.Panel, and I want it to overlay the entire main wx.Panel (not including the menu bar of the wx.Frame). There should be some sort of close button on the help button that will make it disappear again.
What is a good way to achieve this? I've looked into wx.Notebook but haven't found a way to make it not show the tabs.
Note that I don't want to destroy and recreate the help panel every time the user closes and opens it: I just want it to be hidden.
There are several ways
a) you can create a custom child panel, and make it same size and position at 0,0 among top of all child widgets. no need of destroying it just Show/Hide it
this also resizes with parent frame
b) popup a wx.PopupWindow or derived class and place and size it at correct location
so as suggest in a) here is an example, where all controls are put in panel using sizer, as separate help cntrl is created which can be shown/hidden from button, but you can create a custom cntrl which hides itself on clicking close
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.panel = wx.Panel(self)
# create controls
self.cntrlPanel = wx.Panel(self.panel)
stc1 = wx.StaticText(self.cntrlPanel, label="wow it works")
stc2 = wx.StaticText(self.cntrlPanel, label="yes it works")
btn = wx.Button(self.cntrlPanel, label="help?")
btn.Bind(wx.EVT_BUTTON, self._onShowHelp)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(stc1)
sizer.Add(stc2)
sizer.Add(btn)
self.cntrlPanel.SetSizer(sizer)
# create help panel
self.helpPanel = wx.Panel(self.panel)
self.stcHelp = wx.StaticText(self.helpPanel, label="help help help\n"*8)
btn = wx.Button(self.helpPanel, label="close[x]")
btn.Bind(wx.EVT_BUTTON, self._onShowCntrls)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.stcHelp)
sizer.Add(btn)
self.helpPanel.SetSizer(sizer)
self.helpPanel.Hide()
self.helpPanel.Raise()
self.helpPanel.SetBackgroundColour((240,250,240))
self.Bind(wx.EVT_SIZE, self._onSize)
self._onShowCntrls(None)
def _onShowHelp(self, event):
self.helpPanel.SetPosition((0,0))
self.helpPanel.Show()
self.cntrlPanel.Hide()
def _onShowCntrls(self, event):
self.cntrlPanel.SetPosition((0,0))
self.helpPanel.Hide()
self.cntrlPanel.Show()
def _onSize(self, event):
event.Skip()
self.helpPanel.SetSize(self.GetClientSizeTuple())
self.cntrlPanel.SetSize(self.GetClientSizeTuple())
app = wx.PySimpleApp()
frame = MyFrame()
frame.Show()
app.SetTopWindow(frame)
app.MainLoop()
Updated code for Python 3.9.4 and wxPython 4.1.1. Hope people find it useful. The original post was useful to me, but as a neophyte, it took me some effort to have it run properly with recently released tools.
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.panel = wx.Panel(self)
# create controls
self.cntrlPanel = wx.Panel(self.panel)
stc1 = wx.StaticText(self.cntrlPanel, label="wow it works")
stc2 = wx.StaticText(self.cntrlPanel, label="yes it works")
btn = wx.Button(self.cntrlPanel, label="help?")
btn.Bind(wx.EVT_BUTTON, self._onShowHelp)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(stc1)
sizer.Add(stc2)
sizer.Add(btn)
self.cntrlPanel.SetSizer(sizer)
# create help panel
self.helpPanel = wx.Panel(self.panel)
self.stcHelp = wx.StaticText(self.helpPanel, label="help help help\n"*8)
btn = wx.Button(self.helpPanel, label="close[x]")
btn.Bind(wx.EVT_BUTTON, self._onShowCntrls)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.stcHelp)
sizer.Add(btn)
self.helpPanel.SetSizer(sizer)
self.helpPanel.Hide()
self.helpPanel.Raise()
self.helpPanel.SetBackgroundColour((240,250,240))
self.Bind(wx.EVT_SIZE, self._onSize)
self._onShowCntrls(None)
def _onShowHelp(self, event):
self.helpPanel.SetPosition((0,0))
self.helpPanel.Show()
self.cntrlPanel.Hide()
def _onShowCntrls(self, event):
self.cntrlPanel.SetPosition((0,0))
self.helpPanel.Hide()
self.cntrlPanel.Show()
def _onSize(self, event):
event.Skip()
self.helpPanel.SetSize(self.GetClientSize())
self.cntrlPanel.SetSize(self.GetClientSize())
app = wx.App(False)
frame = MyFrame()
frame.Show()
app.SetTopWindow(frame)
app.MainLoop()

Categories