I've installed wxPython on my mac and tried this program
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(200,100))
self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
self.CreateStatusBar() # A Statusbar in the bottom of the window
# Setting up the menu.
filemenu= wx.Menu()
# wx.ID_ABOUT and wx.ID_EXIT are standard IDs provided by wxWidgets.
filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
filemenu.AppendSeparator()
filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
# Creating the menubar.
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content.
self.Show(True)
app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()
I started python2.7 to run it, it prompts a window, but it doesn't show any menubar, out of my expectation.
Did I miss anything in my program, or I need any extra python/wx configurations?
Related
I'm running wxPython 4.0.1 msw (phoenix) with Python 3.6.5 on a Windows7 machine, as well as wxPython 2.9.4 with Python 2.7.
I'm observing an issue with a modal dialog, which doesn't block the access to its parent window behind. This only occurs if I run a progress dialog followed by a modal dialog. This behavior is somehow related to custom dialogs. Integrated dialogs like wx.MessageDialog doesn't seem to have this issue.
To isolate the issue, I've written an example. The first two buttons open either the progress or the modal dialog and work properly. The third button opens both dialogs in sequence. In this case the modal functionality of the custom dialog doesn't work and I'm able to access and close the mainframe. Which causes multiple issues.
Dialog is not modal, the main window can be accessed and closed
import wx
class SomeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, title='SomeDialog',
style=wx.DEFAULT_DIALOG_STYLE)
self.button_ok = wx.Button(self, wx.ID_OK, size=(120,-1))
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(self.button_ok, 0, wx.ALL|wx.ALIGN_CENTER, 10)
self.SetSizer(hsizer)
self.SetSize(self.BestSize)
self.Layout()
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, size=(400, 400))
self.button_progress = wx.Button(self, -1, 'Show Progress')
self.button_modal = wx.Button(self, -1, 'Show Modal')
self.button_both = wx.Button(self, -1, 'Show Both')
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_progress)
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_modal)
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_both)
sizer = wx.BoxSizer()
sizer.Add(self.button_progress)
sizer.Add(self.button_modal)
sizer.Add(self.button_both)
self.SetSizer(sizer)
def on_button(self, event):
if event.EventObject is self.button_progress:
self._show_progress_dialog()
elif event.EventObject is self.button_modal:
self._show_modal_dialog()
else:
self._show_progress_dialog()
self._show_modal_dialog()
def _show_progress_dialog(self):
max = 10
dlg = wx.ProgressDialog('Progress dialog example', 'Some message',
maximum=max, parent=self,
style=wx.PD_APP_MODAL|wx.PD_AUTO_HIDE)
keepGoing = True
count = 0
while keepGoing and count < max:
count += 1
wx.MilliSleep(250)
wx.Yield()
(keepGoing, skip) = dlg.Update(count)
dlg.Destroy()
def _show_modal_dialog(self):
with SomeDialog(self) as dlg:
dlg.CenterOnParent()
dlg.ShowModal()
if __name__ == '__main__':
app = wx.App()
frame = TestFrame()
frame.Show()
app.MainLoop()
In case this is an issue in the wxpython framework and not an issue with my implementation, it would be great if someone could provide me a workaround to show such dialogs in sequence.
This looks like a bug to me. I'm not sure why its happening, but one workaround would be to use wx.CallLater
changing _show_modal_dialog to:
def _show_modal_dialog(self):
def _make_dialog():
with SomeDialog(self) as dlg:
dlg.CenterOnParent()
dlg.ShowModal()
wx.CallLater(50, _make_dialog) # 50 mils is arbitrary
Seems to resolve the issue of the dialog not acting modalish. The problem with this workaround is that it will be non-blocking, meaning that any code that has to wait for the dialog to return has to be moved into the dialog class or passed to the dialog as a callback.
In the meantime I found a workaround myself which I like to share.
I added a handler catching the windows close event.
class TestFrame(wx.Frame):
def __init__(self):
#...
self.Bind(wx.EVT_CLOSE, self.on_close)
This event function checks if some child dialog is open and modal and performs a veto.
def on_close(self, event):
# In case any modal dialog is open, prevent the frame from closing.
for children in (c for c in self.Children if isinstance(c, wx.Dialog)):
if children.IsModal():
event.Veto()
return
event.Skip()
This is also only a workaround, but I seems to work for my use cases.
I'm building a menu for a frame with wxPython (python3).
I want to separate the main.py from the menus.py - have these two separated files so the code is organized into smaller pieces.
I need to be able to pass control back to the main.py from menus.py.
In particular, I need that the handlers for the events I bind for menu items (in menus.py) will reside in main.py, or, alternatively, have the handlers in menus.py, but reference objects in main.py (for example, to Close() the application for the "Exit" menu item.
This is what I have so far, and I tried both ways with no success. How is this achieved?
main.py
import wx
import menus
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "My App Title", size=(1200, 800))
self.panel = wx.Panel(self, wx.ID_ANY)
mainMenu = menus.MainMenu()
mainMenu.build_main_menu(self)
def onExit(self):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()
menus.py
from wx import Menu, MenuBar, MenuEvent, MenuItem, Frame, EVT_MENU
class MainMenu(object):
def build_main_menu(self, frame):
self.fileMenu = Menu()
exitMenuItem = self.fileMenu.Append(101, "E&xit", "Close the application")
menuBar = MenuBar()
menuBar.Append(self.fileMenu, "&File")
frame.Bind(EVT_MENU, MainFrame.onExit(frame), exitMenuItem)
frame.SetMenuBar(menuBar)
return self
You are really close, but just not quite there. For this sort of thing, there is no need to wrap the menu creation code inside a class, so I changed that to just a function. Here's menu.py:
from wx import Menu, MenuBar, MenuEvent, MenuItem, Frame, EVT_MENU
def build_main_menu(frame):
fileMenu = Menu()
exitMenuItem = fileMenu.Append(101, "E&xit", "Close the application")
menuBar = MenuBar()
menuBar.Append(fileMenu, "&File")
frame.Bind(EVT_MENU, frame.onExit, exitMenuItem)
frame.SetMenuBar(menuBar)
Note that you need to call the onExit using the frame instance, not by trying to call it via a class (MainFrame) that you haven't even imported. Also you do not call an event handler in the bind operation. It will get called when the button is pressed.
Next I updated your main.py:
import wx
import menus
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "My App Title", size=(1200, 800))
self.panel = wx.Panel(self, wx.ID_ANY)
mainMenu = menus.build_main_menu(self)
def onExit(self, event):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()
There are two changes here. First we don't need to create an instance of the menu module's class as there is no class any longer. Secondly event handlers take two arguments: self and event.
Now it works!
If I have a wx.Menu (in a wx.MenuBar, at the top of a frame, like normal) - how can I cause that menu to drop down and take focus, without clicking on it. I want the behavior to be as if the user had pressed the keyboard accelerator shortcut for that menu (so Alt+F for example, for the &File menu)
Try with wx.PostEvent:
event = wx.MenuEvent(wx.wxEVT_LEFT_DOWN, menuitem.GetId(), menu)
wx.PostEvent(frame, event)
Other wx mouse events: http://www.wxpython.org/docs/api/wx.MouseEvent-class.html
Found in google groups thread
I had the same requirement and found the simple way of using the PopupMenu function. It is not called from the menu object but from the parent of the menu (the window, frame, etc..)
To make sure that the menu appears at a specific position, regardless of your mouse, supply to the PopupMenu function a position parameter as well.
In the example bellow, I turned a platebtn that was opening the menu only when clicked in the right side, in the small area of the down arrow, into a button that opens the same menu in the same way when you click it anywhere on its surface.
Example:
import wx
import wx.lib.platebtn as platebtn
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wx.Frame.__init__(self, parent, ID, title, size=(300, 250))
wx.Panel(self,-1, style=wx.SUNKEN_BORDER)
droparrow = platebtn.PB_STYLE_DROPARROW | platebtn.PB_STYLE_SQUARE | platebtn.PB_STYLE_GRADIENT
self.btn1 = platebtn.PlateButton(self, wx.ID_ANY, label=" File ", style=droparrow)
self.btn1.SetPressColor(wx.LIGHT_GREY)
self.menu1 = wx.Menu()
self.menu1.Append(1, "New")
self.menu1.Append(2, "Open")
self.menu1.Append(3, "Exit")
sm = wx.Menu()
sm.Append(8, "sub item 1")
sm.Append(9, "sub item 1")
self.menu1.AppendMenu(7, "Test Submenu", sm)
self.btn1.SetMenu(self.menu1)
self.Bind(wx.EVT_BUTTON, self.OnFile, self.btn1)
def OnFile(self, event):
self.btn1.PopupMenu(self.menu1, pos=(1, self.btn1.GetSize()[1]))
app = wx.App(False)
frame = MyFrame(None, -1, "PopupMenu example")
frame.Show()
app.MainLoop()
To define accelerators for menu in your program,Understand through the given example
example:
file_menu=wx.Menu()
menubar=wx.MenuBar()
menubar.Append(file_menu,"&File")
self.SetMenuBar(menubar)
Now we can access the File menu (here) by pressing ALT+F.
If we have other menus too,on pressing ALT ,it will point to the first Letter of menu bar,from which you can press the next key according to the name of menu_item.
I'm trying to test out the wx.Notebook tabs in one of my guis. However, I've got a problem where only a tiny portion of the tab shows in the top left corner.
As you can see, only a very small part of it is showing. I can't figure out what wrong.
My code is as follows:
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, "test", size=(640,480))
panel = wx.Panel(self)
notebook = wx.Notebook(panel, style=wx.BK_DEFAULT, size=(640,480))
tabOne = wx.Panel(notebook)
notebook.AddPage(tabOne, "Tab One")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(notebook, flag=wx.EXPAND)
self.SetSizer(sizer)
if __name__ == '__main__':
app = wx.PySimpleApp(redirect=False)
frame = MyFrame(parent=None, id=-1)
frame.Show()
app.MainLoop()
Anyone know what would cause such a thing?
You have to fix one line:
notebook = wx.Notebook(self, style=wx.BK_DEFAULT, size=(640,480))
Formerly you created the notebook as child of panel which hasn't any sizer set. Hence the small size. panel is now obsolete.
The wxPython ToolBar look and feel does not match that of the current operating system - it has a gradient similar to the Windows Vista / 7 menubar I.E. a silver gradient.
Is there any way to change this so that it blends in with the operating systems look and feel?
Note: There is a style flag that can be set when creating the ToolBar and one of those flags is wx.TB_FLAT but this seems to have no affect on the way the ToolBar is rendered.
I am running my wxPython program on Windows 7.
Edit: Below is a screen shot of what I am seeing.
Edit: It seems the toolbar is drawn in accordance with the current theme as changing to the Windows Classic theme renders a flat toolbar which matches the window background.
The code below shows what I have tried so far. I have created a method called OnPaint which is bound to the toolbars paint event. This has no effect and the toolbar is drawn as in the image above.
I know that the code in OnPaint works as the rectangle is rendered if i bind this method to the windows paint event instead of the toolbars.
import wx
ID_STAT = 1
ID_TOOL = 2
class CheckMenuItem(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(350, 250))
menubar = wx.MenuBar()
file = wx.Menu()
view = wx.Menu()
self.shst = view.Append(ID_STAT, 'Show statubar', 'Show Statusbar', kind=wx.ITEM_CHECK)
self.shtl = view.Append(ID_TOOL, 'Show toolbar', 'Show Toolbar', kind=wx.ITEM_CHECK)
view.Check(ID_STAT, True)
view.Check(ID_TOOL, True)
self.Bind(wx.EVT_MENU, self.ToggleStatusBar, id=ID_STAT)
self.Bind(wx.EVT_MENU, self.ToggleToolBar, id=ID_TOOL)
menubar.Append(file, '&File')
menubar.Append(view, '&View')
self.SetMenuBar(menubar)
self.toolbar = self.CreateToolBar()
self.toolbar.Realize()
self.statusbar = self.CreateStatusBar()
self.Bind(wx.EVT_PAINT, self.OnPaint, self.toolbar)
self.Centre()
self.Show(True)
def OnPaint(self, e):
dc = wx.PaintDC(self)
dc.SetBrush(wx.Brush('#c56c00'))
dc.DrawRectangle(10, 15, 90, 60)
def ToggleStatusBar(self, event):
if self.shst.IsChecked():
self.statusbar.Show()
else:
self.statusbar.Hide()
def ToggleToolBar(self, event):
if self.shtl.IsChecked():
self.toolbar.Show()
else:
self.toolbar.Hide()
app = wx.App()
CheckMenuItem(None, -1, 'Toolbar Test')
app.MainLoop()
It seems the solution to my problem is actually quite simple. Instead of trying to apply some custom paint logic all that was required was a call to the toolbars SetBackgroundColour() method.
The system look and feel can be maintained by using the colours from the wx.SystemSettings class.
self.toolbar.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR))
Does the wxPython demo exhibit the same behavior? I don't remember it not looking correct on my Windows 7 machine. Maybe the manifest file didn't get installed.