Toolbar rendering following wxPython 2.9 upgrade - macos

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!

Related

How to get the size of the wxpython panel

I'm developing a calendar application
The top level window is a frame containing a panel that displays the calendar grid and a panel that contains a "Close" button.
I'm unable to obtain the size of the calendar grid panel.
When I add code to get the panel size, the result is (20,20), which cannot be correct
The screen size is (1920,1080) so I'm expecting something like (1920, 1000)
When I add the wx.lib.inspection module, I see the correct size being displayed. It is (1920, 968)
Can anyone shed some light how to get the correct size of the panel?
This is the code I have so far
import wx
class DrawFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, parent=None, title='Agenda', style= wx.CAPTION | wx.CLOSE_BOX)
self.drawpanel = DrawPanel(self)
self.buttonpanel = ButtonPanel(self)
self.framesizer = wx.BoxSizer(wx.VERTICAL)
self.framesizer.Add(self.drawpanel,1, flag=wx.EXPAND)
# Add an empty space 10 pixels high above and below the button panel
self.framesizer.Add((0,10),0)
self.framesizer.Add(self.buttonpanel,0, flag=wx.EXPAND)
self.framesizer.Add((0,10),0)
self.SetSizer(self.framesizer)
self.SetInitialSize()
self.Maximize()
self.Show()
def GetPanelSize(self):
return self.drawpanel.GetSize()
def OnClose(self, event):
self.Close()
class DrawPanel(wx.Panel):
# This panel's parent is DrawFrame. DrawFrame is the top level window.
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
self.parent = parent
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.x1, self.y1, self.x2, self.y2 = wx.GetClientDisplayRect()
b = self.x1, self.y1, self.x2, self.y2
print b
self.width, self.height = wx.GetDisplaySize()
c = self.width, self.height
print c
def OnPaint(self, event=None):
dc = wx.PaintDC(self)
dc.Clear()
dc.SetPen(wx.Pen(wx.BLACK, 2))
dc.SetBrush(wx.Brush('WHITE'))
"""
DrawRectangle (self, x, y, width, height)
Draw a rectangle with the given corner coordinate and size.
x and y specify the top left corner coordinates and both width and height are positive.
"""
dc.DrawRectangle(self.x1 + 5, self.y1, self.x2 - 10, self.y2 - 60)
dc.DrawLine(40, 100, 600, 100)
class ButtonPanel(wx.Panel):
# This panel's parent is DrawFrame. DrawFrame is the top level window.
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent)
self.parent=parent
self.buttonpanelsizer = wx.BoxSizer(wx.HORIZONTAL)
self.closebutton = wx.Button(self, label = 'Close')
self.Bind(wx.EVT_BUTTON, self.OnClose, self.closebutton)
self.buttonpanelsizer.AddStretchSpacer(prop=1)
self.buttonpanelsizer.Add(self.closebutton, 0, wx.ALIGN_CENTER)
self.SetSizer(self.buttonpanelsizer)
def OnClose(self, event):
self.parent.OnClose(event)
app = wx.App(False)
frame = DrawFrame()
print frame.GetPanelSize()
app.MainLoop()
Much appreciated,
Thanks
You are calling the GetPanelSize too early. Keep in mind that wxPython (and pretty much any GUI framework) is event based. That means that for it to work it must keep processing events, which in case of wxPython means that app.MainLoop() must run. So do not call GetPanelSize before calling app.MainLoop(). Instead, call it when you need it. Do you need it when you paint something? Just use dc.GetSize(). Do you need it elsewhere? Process the wx.EVT_SIZE event and store the current size. Possibly you will have to trigger some action in the EVT_SIZE handler.

Multilpe screen in wxpython

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.

Function to set image of buttons causes image to jump between buttons

I'm trying to teach myself the tkinter module by programming minesweeper. I have created a grid with buttons and a method to set an image flag to cells. It works, in that when you press the right mouse button the image of the button changes as desired, but when you right click on the next button the image just moves to the next button, rather than creating a second flag. I want to be able to place a new flag image on each cell that I right click, rather than just shuffle the image around. Here's my code:
import tkinter as Tk
def main():
root = Tk.Tk()
root.geometry('{}x{}'.format(700, 700))
instance = Minesweeper(root, 10, 10)
root.mainloop()
class Minesweeper:
def __init__(self, parent, height, width):
self.top_frame = Tk.Frame(parent)
self.top_frame.place(anchor=Tk.CENTER, relx=0.5, rely=0.5)
self.frames = []
self.buttons = []
index = 0
for x in range(height):
for y in range(width):
self.frames.append(Tk.Frame(self.top_frame, height=50, width=50))
self.buttons.append(Tk.Button(self.frames[index], bg="white"))
self.frames[index].grid_propagate(False)
self.frames[index].columnconfigure(0, weight=1)
self.frames[index].rowconfigure(0, weight=1)
self.frames[index].grid(row=x, column=y)
self.buttons[index].grid(sticky="wens")
self.buttons[index].bind('<Button-3>', self.flag)
index += 1
def flag(self, event):
self.flag = Tk.PhotoImage(file="flag.png")
event.widget.configure(image=self.flag)
if __name__ == "__main__":
main()
Seems that the below fixed it:
def flag(self, event):
self.flag = Tk.PhotoImage(file="flag.png")
event.widget.image = self.flag # <---- this seemed to fix it
event.widget.configure(image=self.flag)

bind menu events wxpython

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

Running Pyqt UIs in tabs using TabWidget

I want to run my app in different tabs. Rightnow, it is running in main window. I have done some search work on how to create tabs. I found this to be useful, but not sufficient to meet my requirements
Create TAB and create textboxes that take data in the TAB-page
I want to have a feature of adding a new tab (like new tab in chrome)
Below is my code sample. I described what i require in the comments.
from PyQt4 import Qt, QtCore, QtGui
import sys
class Map(QtGui.QMainWindow):
def __init__(self,parentQExampleScrollArea=None,parentQWidget = None):
super(Map,self).__init__()
self.initUI()
#Initialize the UI
def initUI(self):
#Initilizing Menus toolbars
#This must be maintained in all tabbed panes
filename = ""
#Filename is obtained through open file button in file menu
self.filename = filename
def paintEvent(self, e):
qp = QtGui.QPainter()
qp.begin(self)
self.drawPoints(qp,self.filename)
qp.end()
def drawPoints(self, qp,FILENAME=""):
#Read contents in file
#Get the necessary coordinates
#A separate class for storing the info of all the coordinates
#Run a for loop for all the coordinates in the list
#Actually, object is created here and image of that object is set
# as a square using the coordinates
qp.setBrush(QtGui.QColor(255, 0, 20, 200))
qp.drawRect(20,20,75,75)
qp.drawRect(100,20,75,75)
self.update()
#There are many functions to handle keyboard and mouse events
def main():
#How do I modify so that I can have multiple tabs
#And show images of the coordinates in files
#Basically I want to have the feature of opening many files
# and displaying them in UI
#Basically a feature to add a new tab
#like that of in eclipse netbeans sublime etc
app = QtGui.QApplication(sys.argv)
myQExampleScrollArea = Map()
myQExampleScrollArea.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Thanks in advance.. :)
It's simply to use method int QTabWidget.addTab (self, QWidget widget, QString) to create widget in tab. In each tab, I suggest use QtGui.QWidget more than QtGui.QMainWindow;
Example;
import sys
from PyQt4 import QtGui
class QCustomWidget (QtGui.QWidget):
# Your widget to implement
# Put your override method here
def paintEvent (self, eventQPaintEvent):
currentQPainter = QtGui.QPainter()
currentQPainter.begin(self)
currentQPainter.setBrush(QtGui.QColor(255, 0, 20, 200))
currentQPainter.drawRect(20, 20, 75, 75)
currentQPainter.drawRect(100, 20, 75, 75)
self.update()
currentQPainter.end()
class QCustomTabWidget (QtGui.QTabWidget):
def __init__ (self, parent = None):
super(QCustomTabWidget, self).__init__(parent)
self.addTab(QtGui.QPushButton('Test'), 'Tab 1')
self.addTab(QCustomWidget(), 'Tab 2')
myQApplication = QtGui.QApplication([])
myQCustomTabWidget = QCustomTabWidget()
myQCustomTabWidget.show()
sys.exit(myQApplication.exec_())
So handle with more tab, It's bad to create many line call int QTabWidget.addTab (self, QWidget widget, QString). Anyway, All widget has add in QTabWidget is can reference in ifself. So, your can control element in it by call QWidget QTabWidget.widget (self, int index).
Example to call widget in tab widget;
import os
import sys
from PyQt4 import QtCore, QtGui
class QCustomLabel (QtGui.QLabel):
def __init__(self, imagePath, parentQWidget = None):
super(QCustomLabel, self).__init__(parentQWidget)
self.setPixmap(QtGui.QPixmap(imagePath))
class QCustomWidget (QtGui.QWidget):
def __init__ (self, parentQWidget = None):
super(QCustomWidget, self).__init__(parentQWidget)
self.addQPustButton = QtGui.QPushButton('Open image')
self.addQPustButton.setMaximumWidth(120)
self.addQPustButton.released.connect(self.openImage)
self.workSpaceQTabWidget = QtGui.QTabWidget()
self.workSpaceQTabWidget.setTabsClosable(True)
self.workSpaceQTabWidget.tabCloseRequested.connect(self.closeImage)
allQVBoxLayout = QtGui.QVBoxLayout()
allQVBoxLayout.addWidget(self.addQPustButton)
allQVBoxLayout.addWidget(self.workSpaceQTabWidget)
self.setLayout(allQVBoxLayout)
def openImage (self):
path = QtGui.QFileDialog.getOpenFileName(self, 'Open image')
if not path.isEmpty():
self.workSpaceQTabWidget.addTab(QCustomLabel(path), QtCore.QString(os.path.basename(str(path))))
def closeImage (self, currentIndex):
currentQWidget = self.workSpaceQTabWidget.widget(currentIndex)
currentQWidget.deleteLater()
self.workSpaceQTabWidget.removeTab(currentIndex)
myQApplication = QtGui.QApplication([])
myQCustomWidget = QCustomWidget()
myQCustomWidget.show()
sys.exit(myQApplication.exec_())

Resources