On a MacBook running MacOS Mojave 10.14.6, Python 3.7.4 and wxPython 4.0.6 I can't get menus to work when creating a GUI. Here's the code I'm using.
def basicGUI(self):
menuBar = wx.MenuBar()
fileButton = wx.Menu()
exitItem = fileButton.Append(wx.ID_EXIT, 'Exit', 'status msg..')
menuBar.Append(fileButton, 'File')
menuBar.Append(fileButton, 'Edit')
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.Quit, exitItem)
self.SetTitle('Epic Window')
self.Show(True)
Creating the frame and panel is all OK. Any assistance would be appreciated. thanks
I don't own a mac, but running your code on windows produces an assertion error because you're adding the menu named 'fileButton' twice. If you comment out the line menuBar.Append(fileButton, 'Edit') your example should run. If you want to create an edit menu, don't reuse the file menu instance, create a new wx.Menu() instance.
import wx
class Frame(wx.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.basicGUI()
def basicGUI(self):
menuBar = wx.MenuBar()
fileButton = wx.Menu()
editmenu = wx.Menu()
exitItem = wx.MenuItem(fileButton, wx.ID_EXIT, "Exit")
edit_item = wx.MenuItem(editmenu, wx.ID_EDIT, "Edit")
fileButton.Append(exitItem)
editmenu.Append(edit_item)
menuBar.Append(fileButton, 'File')
menuBar.Append(editmenu, 'Edit')
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.Quit, id=wx.ID_EXIT)
self.Bind(wx.EVT_MENU, self.on_edit, id=wx.ID_EDIT)
self.SetTitle('Epic Window')
self.CenterOnScreen(wx.BOTH)
self.Show(True)
def Quit(self, event):
self.Close()
def on_edit(self, event):
with wx.MessageDialog(self, "You clicked edit", "Caption", wx.ICON_INFORMATION) as dialog:
dialog.ShowModal()
app = wx.App()
frame = Frame(parent=None)
app.MainLoop()
Side Note:
It's helpful if you post a runnable example of your problem instead of just the excerpted method so we can see the problem in its full context instead of having to assume what the rest of your program looks like.
Related
all.
I'd like to be able to switch between multiple screens. Meaning, the first one is the main, then when with a button or an external switch is activated I can see the page #2, in that one I may have an other button to return to the first one, or going to #3, etc. Cause I have a main screen for a big RPM meter, but I may want to see instead all three meter on the same page, or view the raw data in an other page, or go to the set-up page or elsewhere in the future development. I'm using the full screen space for my graphic. Maybe something like "hide" or "show" a page with an event of some kind. I have a single class script for every pages so far, but unable to group them in a single one. Thanks for your help
I wrote about this concept several years ago here. I went ahead an reproduced the example from that article:
import wx
import wx.grid as gridlib
class PanelOne(wx.Panel):
""""""
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
txt = wx.TextCtrl(self)
class PanelTwo(wx.Panel):
""""""
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
grid = gridlib.Grid(self)
grid.CreateGrid(25,12)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(grid, 0, wx.EXPAND)
self.SetSizer(sizer)
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY,
"Panel Switcher Tutorial")
self.panel_one = PanelOne(self)
self.panel_two = PanelTwo(self)
self.panel_two.Hide()
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.panel_one, 1, wx.EXPAND)
self.sizer.Add(self.panel_two, 1, wx.EXPAND)
self.SetSizer(self.sizer)
menubar = wx.MenuBar()
fileMenu = wx.Menu()
switch_panels_menu_item = fileMenu.Append(wx.ID_ANY,
"Switch Panels",
"Some text")
self.Bind(wx.EVT_MENU, self.onSwitchPanels,
switch_panels_menu_item)
menubar.Append(fileMenu, '&File')
self.SetMenuBar(menubar)
def onSwitchPanels(self, event):
""""""
if self.panel_one.IsShown():
self.SetTitle("Panel Two Showing")
self.panel_one.Hide()
self.panel_two.Show()
else:
self.SetTitle("Panel One Showing")
self.panel_one.Show()
self.panel_two.Hide()
self.Layout()
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
The basic idea here is to Hide() one panel and Show() another. You might also want to look at the Notebook controls that wxPython provides as they have a similar functionality.
I am new in Python, but I would like to understand the use of GUIs using wxpython. I am using a template for the creation of a frame and I added the menus. The menus are shown but they do not trigger any action, so I need to bind actions to the different menu items I created. Problem is, I don't know how.
I started with the menu save, which I called 'menu_open' and associated to the method
filemenu.Append(wx.ID_OPEN, "Open")
I associated an action using:
self.Bind(wx.EVT_MENU, self.Open, menu_open)
but I got the error:
AttributeError: 'MainWindow' object has no attribute 'Open'
If I try with 'OnOpen' (since there is an 'OnExit' attribute) I get the errors:
frame = MainWindow(None, "Sample editor")
AttributeError: 'MainWindow'object has no attribute 'OnOpen'
So the questions are:
is the self.Bind syntax correct and the right way to assign an action to a menu?
is there a complete list of attributes for the menus available in wxPython?
I am reporting the whole code for reference. Thanks. G.
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import print_function
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)
# A Statusbar in the bottom of the window
self.CreateStatusBar()
# Setting up the menus
'''Define main items'''
filemenu = wx.Menu()
editmenu = wx.Menu()
infomenu = wx.Menu()
'''Items'''
# file menu
menu_open = filemenu.Append(wx.ID_OPEN, "Open")
filemenu.Append(wx.ID_NEW, "New")
filemenu.Append(wx.ID_SAVE, "Save")
filemenu.Append(wx.ID_SAVEAS, "Save as")
filemenu.Append(wx.ID_EXIT, "Exit")
filemenu.AppendSeparator()
filemenu.Append(wx.ID_PRINT, "&Print")
filemenu.Append(wx.ID_PRINT_SETUP, "Print setup")
filemenu.Append(wx.ID_PREVIEW, "Preview")
# edit menu
editmenu.Append(wx.ID_COPY, "Copy")
editmenu.Append(wx.ID_CUT, "Cut")
editmenu.Append(wx.ID_PASTE, "Paste")
editmenu.AppendSeparator()
editmenu.Append(wx.ID_UNDO, "Undo")
editmenu.Append(wx.ID_REDO, "Re-do it")
# info menu
infomenu.Append(wx.ID_ABOUT, "About")
'''Bind items for activation'''
# bind file menu
self.Bind(wx.EVT_MENU, self.OnOpen, menu_open)
# Creating the menubar.
menuBar = wx.MenuBar()
# Add menus
menuBar.Append(filemenu, "&File")
menuBar.Append(editmenu, "&Edit")
menuBar.Append(infomenu, "&Help")
# Adding the MenuBar to the Frame content.
self.SetMenuBar(menuBar)
self.Show(True)
app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()
You simply have not created the event handler method so when using
self.Bind(wx.EVT_MENU, self.OnOpen, menu_open)
you need a method that will be called added to the class MainWindow
def OnOpen(self, event):
print('OnOpen')
I'm sorry if this is so simple, but I'm trying to bind an event to a checkbox in a menubar with wxPython. For some reason, it won't work! I've tried a variety of different ways to get it to print a statement, but nothing happens when I check the box. Is it not binding correctly? This is just a simple app I wrote to demonstrate the problem...
import wx
class Frame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title)
panel = wx.Panel(self)
menuBar = wx.MenuBar()
menu = wx.Menu()
self.checkbox = menu.AppendCheckItem(-1, "Check me")
menuBar.Append(menu,'&check box')
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_CHECKBOX, self.onCheck, self.checkbox)
def onCheck(self, e):
print self.checkbox.IsChecked()
app = wx.App()
test = Frame(None, -1, "Test")
test.Show()
app.MainLoop()
I've figured it out. I needed to change the Bind event from wx.EVT_CHECKBOX to wx.EVT_MENU.
How can I put stuff in the main window? I want to create a line edit in the main window(beneath the menu bar, maybe with some decription laber in front of it). How is this done? I used grid layout and this box layout, nothing works.
(sry for another trivial question, there are only few tutorials on pyside out there, and most of them only cover how to create single windows with buttons ect.)
import sys
from PySide import QtGui, QtCore, QtWebKit
class FirstClass(QtGui.QMainWindow, QtGui.QWidget):
def __init__(self):
super(FirstClass, self).__init__()
self.startingUI()
def startingUI(self):
self.setWindowTitle('Hauptfenster')
self.resize(800, 400)
self.statusBar()
#Menueinstellungen an sich
menue = self.menuBar()
#Actions des Menues:
#datei menue
menuleiste_datei = menue.addMenu('File')
datei_exit = QtGui.QAction('Exit', self)
datei_exit.setStatusTip('Close the programm')
menuleiste_datei.addAction(datei_exit)
datei_exit.triggered.connect(self.close)
#Einstellungen menue
menuleiste_configurations = menue.addMenu('Configurations')
configurations_settings = QtGui.QAction('Settings', self)
configurations_settings.setStatusTip('Configurations(Settings)')
menuleiste_configurations.addAction(configurations_settings)
configurations_settings.triggered.connect(self.newwindow)
self.lineedit = QtGui.QLineEdit()
self.layout = QtGui.QHBoxLayout()
self.layout.addWidget(self.lineedit)
self.setLayout(self.layout)
self.show()
def newwindow(self):
self.wid = QtGui.QWidget()
self.wid.resize(250, 150)
self.setWindowTitle('NewWindow')
self.wid.show()
def main():
app = QtGui.QApplication(sys.argv)
start = FirstClass()
sys.exit(app.exec_())
if __name__== '__main__':
main()
I do not believe creating a class with a multiple inheritance is recommended best practice. If an attribute is not found in FirstClass, then it searches left to right (QtGui.QMainWindow to QtGui.QWidget). From my perspective, this would turn into a nightmare to support and debug. My guess this is why the self.layout is not working properly.
I made separate classes for QtGui.QMainWindow and QtGui.QWidget. FirstWindowClass sets the central widget as FirstWidgetClass. FirstWidgetClass has your QLineEdit and I went ahead and inserted a label. I changed QHBoxLayout to QGridLayout to help you understand how it works.
Some tips from my learning experiences with Python and Pyside these past couple months:
Remember you can always look at PyQt examples and majority will work directly with PySide modules.
I recommend looking over http://srinikom.github.io/pyside-docs/index.html as a lot of the modules have simple examples.
For my personal project, a lot of the solutions to my Qt questions were in C++ so do not be afraid to convert it to python.
import sys
from PySide import QtGui, QtCore, QtWebKit
class FirstWindowClass(QtGui.QMainWindow):
def __init__(self):
super(FirstWindowClass, self).__init__()
self.setWindowTitle('Hauptfenster')
self.resize(800, 400)
self.statusBar()
# Set central widget that expands to fill your window
self.main_widget = FirstWidgetClass(self)
self.setCentralWidget(self.main_widget)
#Menueinstellungen an sich
menue = self.menuBar()
#Actions des Menues:
#datei menue
menuleiste_datei = menue.addMenu('File')
datei_exit = QtGui.QAction('Exit', self)
datei_exit.setStatusTip('Close the programm')
menuleiste_datei.addAction(datei_exit)
datei_exit.triggered.connect(self.close)
#Einstellungen menue
menuleiste_configurations = menue.addMenu('Configurations')
configurations_settings = QtGui.QAction('Settings', self)
configurations_settings.setStatusTip('Configurations(Settings)')
menuleiste_configurations.addAction(configurations_settings)
configurations_settings.triggered.connect(self.newwindow)
# Open the window
self.show()
def newwindow(self):
self.wid = QtGui.QWidget()
self.wid.resize(250, 150)
self.wid.setWindowTitle('NewWindow')
self.wid.show()
class FirstWidgetClass(QtGui.QWidget):
def __init__(self, parent=None):
super(FirstWidgetClass, self).__init__()
self.label_example = QtGui.QLabel('Enter Data:')
self.lineedit = QtGui.QLineEdit()
self.layout = QtGui.QGridLayout()
self.layout.addWidget(self.label_example, 0, 0)
self.layout.addWidget(self.lineedit, 0, 1)
self.setLayout(self.layout)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
start = FirstWindowClass()
sys.exit(app.exec_())
if __name__== '__main__':
main()
This code worked fine on wxPython 2.8, following an upgrade today to 2.9 however the toolbar doesn't
display at all. If I remove the self.SetToolBar() call the icon does show up but not as a button, and the toolbar formatting doesn't stretch when the screen is re-sized. Any ideas?
import wx
class MyApp(wx.App):
def OnInit(self):
self.frame = Example(None, title="Word Bag", size=(400,100))
self.SetTopWindow(self.frame)
self.frame.Show()
return True
class MyToolbar(wx.ToolBar):
"""Toolbars are attached to frames, so need TBar = Toolbar(self) in frame init"""
def __init__(self, parent):
wx.ToolBar.__init__(self, parent)
# set my preferred default size for icons
self.SetToolBitmapSize((32,32))
# the main bit where icons are formatted, added, and bound to handlers
self.initialiseIcons()
# Need to call realise before exiting
self.Realize()
def initialiseIcons(self):
"""Iterate over icons and add them to toolbar"""
for each in self.toolbarData():
self.createSimpleTool(*each)
def createSimpleTool(self, label, filename, statbar, handler):
"""Adds icons to bar using AddSimpleTool"""
if not label:
self.AddSeparator()
return
bmp = wx.Image(filename, wx.BITMAP_TYPE_PNG).ConvertToBitmap()
tool = self.AddSimpleTool(-1, bmp, label, statbar)
self.Bind(wx.EVT_MENU, handler, tool)
def toolbarData(self):
"""Put your icon data here in the following format...
[0] = tooltip label, [1] = bitmap path, [2] = status bar label, [3] = bound function"""
return [["Add new word","/Users/paulpatterson/Desktop/add.png","Add a new word to the dictionary",self.OnAddWord]]
# toolbar icon handlers here...
def OnAddWord(self, event):
pass
def OnRemoveWord(self, event):
pass
def OnSearchWord(self, event):
pass
class Example(wx.Frame):
def __init__(self, parent, title, size):
super(Example, self).__init__(parent, title=title, size=size)
# Create and set the toolbar
tBar = MyToolbar(self)
self.SetToolBar(tBar)
self.frameSizer = wx.BoxSizer(wx.VERTICAL)
self.panelOne = MyPanel(self)
self.frameSizer.Add(self.panelOne, 1, wx.EXPAND)
self.SetSizer(self.frameSizer)
#self.frameSizer.Fit(self)
self.Centre()
self.Show()
class MyPanel(wx.Panel):
def __init__(self, parent):
super(MyPanel, self).__init__(parent)
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
### widgets here
# set optimum layout for mainsizer...
self.SetSizer(self.mainSizer)
# ...then fit main sizer to the panel.
self.mainSizer.Fit(self)
if __name__ == '__main__':
app = MyApp(False)
app.MainLoop()
I had the same problem just hours ago. I couldn't get a custom created toolbar to work, but using Frame.CreateToolBar() works as expected. Example:
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(
self, parent=None, id=-1, title='Test window',
size=wx.Size(800, 600)
)
self.setup_toolbar()
def setup_toolbar(self):
# First create the toolbar.
self.toolbar = self.CreateToolBar(wx.TB_FLAT | wx.TB_TEXT)
self.Bind(wx.EVT_TOOL, self.on_toolbar)
# Add a 'Clear all' button.
self.toolbar.AddLabelTool(
wx.ID_NEW, 'Clear all', get_toolbar_art('new_big'),
shortHelp='Remove all the contents from the text inputs.'
)
# Add an 'Open' button.
self.toolbar.AddLabelTool(
wx.ID_OPEN, 'From file...', get_toolbar_art('open_big'),
shortHelp='Fill the input box with the ' +
'contents of a Linjekort text file.'
)
# self.toolbar.AddSeparator() # A separator.
# Add a 'Save all' button.
self.toolbar.AddLabelTool(
wx.ID_SAVE, 'Save results to...', get_toolbar_art('save_big'),
shortHelp='Save all the Ozi files to a directory.'
)
self.toolbar.Realize()
def get_toolbar_art(name):
return wx.Bitmap('icons/{}.png'.format(name))
But this doesn't answer how to get a custom toolbar subclass to work. Have you tried to just add the toolbar to your layout using a sizer, not using the SetToolBar function? That's the only way I know of to avoid ending up with the OSX native Frame toolbar. Here's an example of that done:
def create_output_panel(self, parent, slug, label):
panel = wx.Panel(parent, style=wx.BORDER_THEME)
panel.SetBackgroundColour(
wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW)
)
# Toolbar.
toolbar = wx.ToolBar(panel, -1, style=wx.BORDER_RAISED)
toolbar.AddSimpleTool(
wx.ID_SAVE, get_toolbar_art('save'),
shortHelpString='Save to file...'
)
toolbar.Realize()
toolbar.Bind(wx.EVT_TOOL, lambda evt: print('Success'))
# Text control.
textctrl = wx.TextCtrl(
panel, -1, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.BORDER_NONE
)
# Organize controls.
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(
wx.StaticText(panel, -1, label), proportion=0, border=5, flag=wx.ALL
)
sizer.Add(toolbar, proportion=0, flag=wx.EXPAND)
sizer.Add(textctrl, proportion=1, flag=wx.EXPAND)
panel.SetSizer(sizer)
return panel
And a screen shot:
I hope this helps!