bind menu events wxpython - drop-down-menu

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')

Related

wxPython build menus on MacBook

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.

QComboBox popup animation glitch

When using a QComboBox in PySide2 the popup menu seems to initially start about 10 pixels or so to the left until its finished animating down at which point it pops (about) 10 pixels to the right into the correct position.
How can I fix this? Or am I able to disable the animation so the menu just opens without animating? And am I able to control the animation time for the popup?
Here are two screenshots, the top one is while the combobox dropdown is animating down and the bottom one is after the dropdown is open:
Here's the simple example code use to produce the combobox above:
from PySide2 import QtCore, QtWidgets
import sys
class MyDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.setWindowTitle('Modal Dialogs')
self.setMinimumSize(300,80)
# remove help icon (question mark) from window
self.setWindowFlags(self.windowFlags() ^ QtCore.Qt.WindowContextHelpButtonHint)
# create widgets, layouts and connections (signals and slots)
self.create_widgets()
self.create_layouts()
self.create_connections()
def create_widgets(self):
self.combo = QtWidgets.QComboBox()
self.combo.addItems(['one','two','three'])
def create_layouts(self):
# self must be passed to the main_layout so it is parented to the dialog instance
main_layout = QtWidgets.QVBoxLayout(self)
main_layout.addWidget(self.combo)
def create_connections(self):
pass
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
my_dialog = MyDialog()
my_dialog.show() # Show the UI
sys.exit(app.exec_())

CheckBox Event in wxPython not working

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.

Popup window on button click

I want to create a code which will display a window with a button, which clicked will create another window with some fields (like QLabel, QLineEdit, QSpinBox, etc.). However, I don't know how to create that popup window...
Here is my code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys # Needed for PySide
from PySide.QtCore import *
from PySide.QtGui import *
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
# Create widgets
self.label1 = QLabel("Label1")
self.button_open = QPushButton("Open popup")
self.button = QPushButton("Go!")
self.qbtn = QPushButton('Quit')
# Create layout and add widgets
layout = QVBoxLayout()
layout.addWidget(self.label1)
layout.addWidget(self.button_open)
# Buttons layout
hbox_buttons = QHBoxLayout()
hbox_buttons.addStretch(1)
hbox_buttons.addWidget(self.button)
hbox_buttons.addWidget(self.qbtn)
# Main layout
layout.addStretch(1)
layout.addWidget(self.button_open)
layout.addLayout(hbox_buttons)
self.setLayout(layout)
# Add buttons slots
self.button_open.clicked.connect(self.popup)
self.button.clicked.connect(self.function_runner)
self.qbtn.clicked.connect(QCoreApplication.instance().quit)
def popup (self, parent=__init__):
new_win = # I wonder what should be here
if __name__ == '__main__':
# Create the Qt Application
app = QApplication(sys.argv)
# Create and show the form
form = Form()
form.show()
# Run the main Qt loop
sys.exit(app.exec_())
I don't know if this is the best way, but the one I could figure out over the night... I hope it will help someone who got stuck with a similar problem.
So, I (simply) created a separate code for that second window and called it with
from subprocess import call
call("./my_2nd_window_code.py")

Toolbar rendering following wxPython 2.9 upgrade

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!

Resources