I'm trying to find a way to hide the menubar when the mouse isnt over it, i know i could get the mouse xy position and check if its in a certain area but im having no luck hiding the menubar itself.
My code:
class HelloFrame(wx.Frame):
images = []
def __init__(self, *args, **kw):
# ensure the parent's __init__ is called
super(HelloFrame, self).__init__(*args, **kw)
# create a panel in the frame
pnl = wx.Panel(self)
# create a menu bar
self.makeMenuBar()
# and a status bar
#self.CreateStatusBar()
#self.SetStatusText("Welcome to wxPython!")
def makeMenuBar(self):
fileMenu = wx.Menu()
helloItem = fileMenu.Append(-1, "&Load Backround Image")
fileMenu.AppendSeparator()
exitItem = fileMenu.Append(wx.ID_EXIT)
helpMenu = wx.Menu()
aboutItem = helpMenu.Append(wx.ID_ABOUT)
menuBar = wx.MenuBar()
menuBar.Append(fileMenu, "&File")
#menuBar.Append(OptionsMenu, "&Options") TBM
menuBar.Append(helpMenu, "&Help")
# Give the menu bar to the frame
self.SetMenuBar(menuBar)
# Finally, associate a handler function with the EVT_MENU event for
# each of the menu items. That means that when that menu item is
# activated then the associated handler function will be called.
self.Bind(wx.EVT_MENU, self.OpenImage, helloItem)
self.Bind(wx.EVT_MENU, self.OnExit, exitItem)
self.Bind(wx.EVT_MENU, self.OnAbout, aboutItem)
def OnExit(self, event):
self.Close(True)
def OnAbout(self, event):
wx.MessageBox("Test",
"About",
wx.OK|wx.ICON_INFORMATION)
def OpenImage(self, event):
with wx.FileDialog (None, "Choose image", wildcard="PNG files (*.png)|*.png", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
return # the user changed their mind
else:
# Gets rid of other images
for i in self.images:
i.Destroy()
self.images.remove(i)
# Displays image and adds image object to [self.images]
ImageName = fileDialog.GetPath()
png = wx.Image(ImageName, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.images.append(wx.StaticBitmap(self, -1, png, (0, 0), (png.GetWidth(), png.GetHeight())))
print(ImageName)
if __name__ == '__main__':
app = wx.App()
frm = HelloFrame(None, title='FileHeader Test')
frm.Show()
app.MainLoop()
ive tried just adding menubar.Hide() in the function makemenubar but to no avail
ive also tried self.Hide() in the same area but no luck
I should start by saying that I don't recommend that you do this.
It's non-standard and could confuse.
It's expensive cpu-wise for no obvious benefit.
That said, here is one way of doing it, using Show, Hide and EVT_MOTION.
The issue is that once it is hidden/removed, the next mouse OVER doesn't happen because it is no longer there. As you suppose, we can assign a territorial range where we suppose it would be. If the cursor strays into that zone, the menu can be shown or hidden.
import wx
class HelloFrame(wx.Frame):
images = []
def __init__(self, *args, **kw):
# ensure the parent's __init__ is called
super(HelloFrame, self).__init__(*args, **kw)
# create a panel in the frame
pnl = wx.Panel(self)
pnl2 = wx.Panel(self)
pnl.SetBackgroundColour("green")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(pnl,1, wx.EXPAND,10)
sizer.Add(pnl2,1, wx.EXPAND,10)
self.SetSizer(sizer)
# create a menu bar
self.makeMenuBar()
# and a status bar
#self.CreateStatusBar()
#self.SetStatusText("Welcome to wxPython!")
pnl.Bind(wx.EVT_MOTION, self.MenuPos)
def makeMenuBar(self):
fileMenu = wx.Menu()
helloItem = fileMenu.Append(-1, "&Load Backround Image")
fileMenu.AppendSeparator()
exitItem = fileMenu.Append(wx.ID_EXIT)
helpMenu = wx.Menu()
aboutItem = helpMenu.Append(wx.ID_ABOUT)
self.menuBar = wx.MenuBar()
self.menuBar.Append(fileMenu, "&File")
self.menuBar.Append(helpMenu, "&Help")
# Give the menu bar to the frame
self.SetMenuBar(self.menuBar)
self.menuBar.Hide()
# Finally, associate a handler function with the EVT_MENU event for
# each of the menu items. That means that when that menu item is
# activated then the associated handler function will be called.
self.Bind(wx.EVT_MENU, self.OpenImage, helloItem)
self.Bind(wx.EVT_MENU, self.OnExit, exitItem)
self.Bind(wx.EVT_MENU, self.OnAbout, aboutItem)
def MenuPos(self, event):
x, y = event.GetPosition()
if y <= 2:
self.menuBar.Show()
if y >= 3:
self.menuBar.Hide()
event.Skip()
def OnExit(self, event):
self.Close(True)
def OnAbout(self, event):
wx.MessageBox("Test",
"About",
wx.OK|wx.ICON_INFORMATION)
def OpenImage(self, event):
with wx.FileDialog (None, "Choose image", wildcard="PNG files (*.png)|*.png", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
return # the user changed their mind
else:
# Gets rid of other images
for i in self.images:
i.Destroy()
self.images.remove(i)
# Displays image and adds image object to [self.images]
ImageName = fileDialog.GetPath()
png = wx.Image(ImageName, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.images.append(wx.StaticBitmap(self, -1, png, (0, 0), (png.GetWidth(), png.GetHeight())))
print(ImageName)
if __name__ == '__main__':
app = wx.App()
frm = HelloFrame(None, title='FileHeader Test')
frm.Show()
app.MainLoop()
Related
I've been working on an application for blind people, so I decided to python and wxPython for the GUI. The problem is that I cannot navigate the wx.toolbar using tabs. This is my actual code.
menuToolBar = self.CreateToolBar(style=wx.TB_FLAT)
menuToolBar.AddSeparator()
menuToolBar.AddTool(ID_NUEVO, "Nuevo Archivo", wx.Bitmap(
os.path.join(self.__rutaPrincipal, "img/nueva pagina.png")), "Nuevo Archivo")
menuToolBar.AddSeparator()
menuToolBar.AddTool(ID_GUARDAR_ARCHIVO, "Guardar Archivo", wx.Bitmap(
os.path.join(self.__rutaPrincipal, "img/guardar pagina.png")), "Guardar Archivo")
menuToolBar.AddTool(ID_ABRIR_ARCHIVO, "Abrir Página", wx.Bitmap(
os.path.join(self.__rutaPrincipal, "img/abrir pagina.png")), "Abrir Página")
menuToolBar.AddSeparator()
menuToolBar.AddTool(ID_FOCUS_PANEL_ESCRITURA, "Foco Escritura", wx.Bitmap(
os.path.join(self.__rutaPrincipal, "img/panel escritura.png")), "Foco Escritura")
menuToolBar.AddTool(ID_FOCUS_PANEL_RESULTADO, "Foco Resultado", wx.Bitmap(
os.path.join(self.__rutaPrincipal, "img/panel resultado.png")), "Foco Resultado")
menuToolBar.AddSeparator()
menuToolBar.AddTool(ID_CERRAR_PAGINA, "Cerrar Página", wx.Bitmap(
os.path.join(self.__rutaPrincipal, "img/cerrar pagina.png")), "Cerrar Página")
menuToolBar.AddSeparator()
menuToolBar.Realize()
is there any configuration to achieve it? or maybe is there another component that help me?.
P.D. I am new to python development and Stackoverflow
Rather than delete the existing anwser, which in fact answers a different question, I'm leaving it in place because I personally believe it's useful, in a related way.
Here is an example of what I was referring to in my comment, about making your own toolbar, which you can navigate using Tab, Left and Right Arrow keys.
It should be enough to get you started.
It uses a normal button and some Generic bitmap buttons, both will work, as we need to illustrate which button currently has focus, which is achieved by using at least 2 different bitmaps for each button. One Normal and one for Focus.
import wx
import wx.lib.buttons as buttons
Valid_keys = [wx.WXK_F1,wx.WXK_RETURN,wx.WXK_TAB,wx.WXK_LEFT,wx.WXK_RIGHT]
class MainFrame(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
self.funcs={"Button1":self.OnButton1,
"Button2":self.OnButton2,
"Button3":self.OnButton3,
"Button4":self.OnButton4}
wx.Frame.__init__(self, None, title="Self defined Toolbar", size=(400,400))
tool_panel = wx.Panel(self,-1,name="tool_panel")
tool_panel.SetBackgroundColour("lightgreen")
tool_panel.SetToolTip("Press F1 or Tab to Access Toolbar")
panel = wx.Panel(self,-1,size=(400,300), name="Main_panel")
panel.SetBackgroundColour("lightblue")
bmp = wx.Bitmap("Discord.png", wx.BITMAP_TYPE_ANY)
bmp1 = wx.Bitmap("Discord1.png", wx.BITMAP_TYPE_ANY)
bmp2 = wx.Bitmap("Discord2.png", wx.BITMAP_TYPE_ANY)
self.Button1 = buttons.GenBitmapButton(tool_panel,bitmap=bmp,size=(40,40),name="Button1")
self.Button2 = buttons.GenBitmapButton(tool_panel,bitmap=bmp,size=(40,40),name="Button2")
self.Button3 = buttons.GenBitmapButton(tool_panel,bitmap=bmp,size=(40,40),name="Button3")
self.Button4 = wx.Button(tool_panel,-1, label="",size=(40,40),name="Button4")
self.Button4.SetBitmap(wx.Bitmap('test.png'))
self.Button4.SetBitmapFocus(wx.Bitmap('testfocus.png'))
self.Text = wx.TextCtrl(panel, -1, size=(400,300), style=wx.TE_MULTILINE)
self.Button1.SetToolTip("Function1")
self.Button2.SetToolTip("Function2")
self.Button3.SetToolTip("Function3")
self.Button4.SetToolTip("Function4")
self.BitmapButtons = [self.Button1,self.Button2,self.Button3]
for i in range(0,len(self.BitmapButtons)):
self.BitmapButtons[i].SetBitmapLabel(bmp)
self.BitmapButtons[i].SetBitmapFocus(bmp2)
self.BitmapButtons[i].SetBitmapSelected(bmp1)
self.Bind(wx.EVT_BUTTON, self.OnButton)
self.Bind(wx.EVT_CHAR_HOOK, self.OnKey)
tool_sizer=wx.BoxSizer(wx.HORIZONTAL)
main_sizer=wx.BoxSizer(wx.VERTICAL)
tool_sizer.Add(self.Button1)
tool_sizer.Add(self.Button2)
tool_sizer.Add(self.Button3)
tool_sizer.Add(self.Button4)
tool_panel.SetSizer(tool_sizer)
main_sizer.Add(tool_panel,0,wx.EXPAND)
main_sizer.Add(panel,0,wx.EXPAND,0)
self.SetSizer(main_sizer)
self.Text.SetFocus()
self.Show()
def OnButton(self, event):
Id = event.GetId()
btn = event.GetEventObject()
x = self.funcs[btn.GetName()]
x()
self.Text.SetFocus()
def OnButton1(self):
print("Button 1")
def OnButton2(self):
print("Button 2")
def OnButton3(self):
print("Button 3")
def OnButton4(self):
print("Button 4")
def OnKey(self, event):
key = event.GetKeyCode()
if key not in Valid_keys: #Ignore all keys except F1, Enter and Navigation keys
event.Skip()
return
if key not in [wx.WXK_F1,wx.WXK_RETURN]:
event.Skip()
return
if key == wx.WXK_F1: #F1 focuses on the First Toolbar button
self.Button1.SetFocus()
return
i = self.get_focus()
if i == 1:
id="Button1"
elif i == 2:
id="Button2"
elif i == 3:
id="Button3"
elif i == 4:
id="Button4"
if i > 0: #Focus was a toolbar button, execute button function
x = self.funcs[id]
x()
self.Text.SetFocus()
event.Skip()
def get_focus(self):
focused = wx.Window.FindFocus()
if focused == self.Button1:
return 1
elif focused == self.Button2:
return 2
elif focused == self.Button3:
return 3
elif focused == self.Button4:
return 4
else:#Something other than the toolbar buttons has focus return Fail
return -1
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App()
frame = MainFrame()
app.MainLoop()
You need to implement Accelerators in your MenuBar and possibly your MenuItem's.
An accelerator table allows the application to specify a table of keyboard shortcuts for menu or button commands.
Often, these are set up using the text in the menu item, by-passing the wx.AcceleratorTable.
p1 = wx.MenuItem(playm, wx.NewIdRef(), '&Play\tCtrl+P')
Here the &Play sets P to be the Menu accelerator, and Ctrl+P to be the hotkey for the function represented by the menu item.
Put another way, when navigating the menus, pressing P will select the Play menu item. When not navigating the menus, just pressing Ctrl+P will run that function.
N.B.
You do not have to setup both.
Do not place the & ampersand character before the same letter more than once in a single menu or the menubar, as only the first one will function.
e.g. "&Save" and "Sa&ve as" would work, using S for "Save" and V for "Save as"
To access the Menu shortcuts Press the Alt button and then any of the underlined letters.
Some minimal code with Menu accelerators but no assigned hotkeys:
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, '&Quit', 'Quit application')
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.OnExit, id=wx.ID_EXIT)
self.Bind(wx.EVT_MENU, self.OnOpen)
# 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)
def OnOpen(self, event):
print ("Menu item selected")
# event skip as we want to continue checking events in case Quit was chosen
event.Skip()
def OnExit(self, event):
self.Destroy()
app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()
Run this code and Press the Alt key, press one of the undelined letters F, E or H and navigate the menus using the arrow keys or any underlined letters.
N.B. Certain functions are automatically assigned Hotkeys e.g. Ctrl+Q is Quit.
Premise:
I'm creating a little wx.Frame with a wx.StatusBar with 3 Fields, the one on the left and the one on the right smaller and the one on the center bigger.
Now, By Default, when i put the mouse on a wx.MenuItem the wx.StatusBar set as Status Text the wx.MenuItem's Help String in the FIRST wx.StatusBar's Field like in the below picture:
Question:
Now, I want, if it's possible, that in the above situation the wx.MenuItem's Help String in the SECOND FIELD (the biggest one in the center)
You are looking for SetStatusBarPane() for the frame
It simply requires you to nominate a pane number (from 0) where the text should be displayed.
import wx
class Test(wx.Frame):
def __init__(self,parent):
wx.Frame.__init__(self,parent,title="Frame aka Window",size = (300,200))
panel = wx.Panel(self)
self.status=self.CreateStatusBar(3)
self.SetStatusBarPane(1)
self.status.SetStatusText("Status bar 0",0)
self.status.SetStatusText("Status bar 2",2)
menubar=wx.MenuBar()
firstm=wx.Menu()
secondm=wx.Menu()
fm1 = wx.MenuItem(firstm, wx.NewIdRef(), 'New Window\tAlt+N')
firstm.Append(fm1)
self.Bind(wx.EVT_MENU, self.OnMenu1, id=fm1.GetId())
fm2 = wx.MenuItem(firstm, wx.NewIdRef(), 'Open', "Text for the statusbar")
firstm.Append(fm2)
self.Bind(wx.EVT_MENU, self.OnMenu2, id=fm2.GetId())
fm3 = wx.MenuItem(firstm, -1, 'Quit\tAlt+Q')
firstm.Append(fm3)
self.Bind(wx.EVT_MENU, self.OnMenu3, id=fm3.GetId())
sm1 = wx.MenuItem(firstm, wx.ID_ANY, 'Re-Do', "Statusbar Re-Do")
secondm.Append(sm1)
self.Bind(wx.EVT_MENU, self.OnsMenu1, id=sm1.GetId())
sm2 = wx.MenuItem(secondm, wx.ID_ANY, 'Un-Do', "Statusbar Un-Do")
secondm.Append(sm2)
self.Bind(wx.EVT_MENU, self.OnsMenu2, id=sm2.GetId())
menubar.Append(firstm,"File")
menubar.Append(secondm,"Edit")
self.SetMenuBar(menubar)
t = wx.StaticText(panel,-1,"Hello i'm a test", pos=(10,20))
def OnMenu1(self, event):
print("Menu item 1",event.GetId())
def OnMenu2(self, event):
print("Menu item 2",event.GetId())
def OnMenu3(self, event):
print("Menu item 3 Quit",event.GetId())
self.Destroy()
def OnsMenu1(self, event):
print("2nd Menu item 1",event.GetId())
def OnsMenu2(self, event):
print("2nd Menu item 2",event.GetId())
if __name__=='__main__':
app=wx.App()
frame=Test(None)
frame.Show()
app.MainLoop()
I'm currently learning to program with Python, I got this example code for a simple image viewer (see below), which produces the following error message when I close the app, run it again and open any directory to browse the images in it:
File "...\image_viewer4.py", line 103, in update_photo
self.image_ctrl.SetBitmap(wx.Bitmap(img))
RuntimeError: wrapped C/C++ object of type StaticBitmap has been
deleted.
This happens every time unless I restart the kernel in Spyder.
I already read through PyQt: RuntimeError: wrapped C/C++ object has been deleted, Understanding the “underlying C/C++ object has been deleted” error, and Can a PyQt4 QObject be queried to determine if the underlying C++ instance has been destroyed?, but I don't understand what causes the error in the code example above. I mean, the error states that the bitmap object is deleted but i have no clue as to how or why.
I appreciate any productive help anyone can give me. I'd like to understand what exactly causes the error in the code and how to avoid it.
Here is the code:
import glob
import os
import wx
from pubsub import pub
class ImagePanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
disW, disH = wx.DisplaySize()
self.max_size = 800
self.photos = []
self.current_photo = 0
self.total_photos = 0
self.layout()
pub.subscribe(self.update_photos_via_pubsub, "update")
def layout(self):
"""
Layout the widgets on the panel
"""
self.main_sizer = wx.BoxSizer(wx.VERTICAL)
btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
img = wx.Image(self.max_size, self.max_size)
self.image_ctrl = wx.StaticBitmap(self, wx.ID_ANY,
wx.Bitmap(img))
self.main_sizer.Add(self.image_ctrl, 0, wx.ALL|wx.CENTER, 5)
self.image_label = wx.StaticText(self, label="")
self.main_sizer.Add(self.image_label, 0, wx.ALL|wx.CENTER, 5)
btn_data = [("Previous", btn_sizer, self.on_previous),
("Next", btn_sizer, self.on_next)]
for data in btn_data:
label, sizer, handler = data
self.btn_builder(label, sizer, handler)
self.main_sizer.Add(btn_sizer, 0, wx.CENTER)
self.SetSizer(self.main_sizer)
def btn_builder(self, label, sizer, handler):
"""
Builds a button, binds it to an event handler and adds it to a sizer
"""
btn = wx.Button(self, label=label)
btn.Bind(wx.EVT_BUTTON, handler)
sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
def on_next(self, event):
"""
Loads the next picture in the directory
"""
if not self.photos:
return
if self.current_photo == self.total_photos - 1:
self.current_photo = 0
else:
self.current_photo += 1
self.update_photo(self.photos[self.current_photo])
def on_previous(self, event):
"""
Displays the previous picture in the directory
"""
if not self.photos:
return
if self.current_photo == 0:
self.current_photo = self.total_photos - 1
else:
self.current_photo -= 1
self.update_photo(self.photos[self.current_photo])
def update_photos_via_pubsub(self, photos):
self.photos = photos
self.total_photos = len(self.photos)
self.update_photo(self.photos[0])
self.current_photo = 0
def update_photo(self, image):
"""
Update the currently shown photo
"""
img = wx.Image(image, wx.BITMAP_TYPE_ANY)
# scale the image, preserving the aspect ratio
W = img.GetWidth()
H = img.GetHeight()
if W > H:
NewW = self.max_size
NewH = self.max_size * H / W
else:
NewH = self.max_size
NewW = self.max_size * W / H
img = img.Scale(NewW, NewH)
self.image_ctrl.SetBitmap(wx.Bitmap(img))
self.Refresh()
def reset(self):
img = wx.Image(self.max_size,
self.max_size)
bmp = wx.Bitmap(img)
self.image_ctrl.SetBitmap(bmp)
self.current_photo = 0
self.photos = []
class MainFrame(wx.Frame):
def __init__(self):
disW, disH = wx.DisplaySize()
super().__init__(None, title='Image Viewer',
size=(disW-500, disH-100))
self.panel = ImagePanel(self)
# create status bar
self.CreateStatusBar()
# create the menu bar
menuBar = wx.MenuBar()
# create a menu
fileMenu = wx.Menu()
openBut = wx.MenuItem(fileMenu, wx.ID_OPEN, '&Open', 'Open Working Directory')
fileMenu.Append(openBut)
fileMenu.Bind(wx.EVT_MENU, self.on_open_directory, openBut)
# add a line separator to the file menu
fileMenu.AppendSeparator()
# add a quit button to fileMenu
quitBut = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit', 'Exit the Programm')
fileMenu.Append(quitBut)
# connect the quit button to the actual event of quitting the app
fileMenu.Bind(wx.EVT_MENU, self.onQuit, quitBut)
# give the menu a title
menuBar.Append(fileMenu, "&File")
# connect the menu bar to the frame
self.SetMenuBar(menuBar)
self.create_toolbar()
self.Show()
def create_toolbar(self):
"""
Create a toolbar
"""
self.toolbar = self.CreateToolBar()
self.toolbar.SetToolBitmapSize((16,16))
open_ico = wx.ArtProvider.GetBitmap(
wx.ART_FILE_OPEN, wx.ART_TOOLBAR, (16,16))
openTool = self.toolbar.AddTool(
wx.ID_ANY, "Open", open_ico, "Open an Image Directory")
self.Bind(wx.EVT_MENU, self.on_open_directory, openTool)
self.toolbar.Realize()
def on_open_directory(self, event):
"""
Open a directory dialog
"""
with wx.DirDialog(self, "Choose a directory",
style=wx.DD_DEFAULT_STYLE) as dlg:
if dlg.ShowModal() == wx.ID_OK:
self.folder_path = dlg.GetPath()
photos = glob.glob(os.path.join(self.folder_path, '*.jpg'))
if photos:
pub.sendMessage("update", photos=photos)
else:
self.panel.reset()
def onQuit(self, event):
# get the frame's top level parent and close it
wx.GetTopLevelParent(self).Destroy()
if __name__ == '__main__':
app = wx.App(redirect=False)
frame = MainFrame()
app.MainLoop()
In case they are needed, here is the traceback:
File "...\image_viewer4.py", line 183, in on_open_directory
pub.sendMessage("update", photos=photos)
File "C:\Anaconda\lib\site-packages\pubsub\core\publisher.py", line 216, in sendMessage
topicObj.publish(**msgData)
File "C:\Anaconda\lib\site-packages\pubsub\core\topicobj.py", line 452, in publish
self.__sendMessage(msgData, topicObj, msgDataSubset)
File "C:\Anaconda\lib\site-packages\pubsub\core\topicobj.py", line 482, in __sendMessage
listener(data, self, allData)
File "C:\Anaconda\lib\site-packages\pubsub\core\listener.py", line 237, in __call__
cb(**kwargs)
File "...\image_viewer4.py", line 84, in update_photos_via_pubsub
self.update_photo(self.photos[0])
It isn't immediately obvious but I notice that you don't cancel your pubsub subscription.
Try:
def onQuit(self, event):
# get the frame's top level parent and close it
pub.unsubscribe(self.panel.update_photos_via_pubsub, "update")
wx.GetTopLevelParent(self).Destroy()
See if that makes a difference.
To ensure that onQuit is also executed when closing the application by using the X in the top right of the frame, use the following (in MainFrame) :
self.Bind(wx.EVT_CLOSE, self.onQuit)
That should cover all the bases.
That may be the same issue as in
Defining a wx.Panel destructor in wxpython
It may come from the fact that a deleted window, containing the pub.subscribe(), answers the pub.sendMessage().
To avoid this you can try to put in your method the following:
def update_photo(self, image):
if self.__nonzero__():
do something
...
you can see wx.PyDeadObjectError –> RuntimeError in following documentation:
https://wxpython.org/Phoenix/docs/html/MigrationGuide.html
I am programming an application using wxpython and wx.lib.pubsub. in python 2.7.3
1- There is a Frame with a menu item. When this menu is clicked, a message is published by pubsub.
2- This message destroys (if possible) and creates a "first level" dialogue.
3- "First Level" dialogue has an list of valules and an "add value" button. (NOTE: Such list of variables can be modified so I am trying to update this list)
4- When the "add value" button is clicked, another message is published by pubsub.
5- This message creates a "Second Level" dialogue, so a new name for the new variable can be written.
6- There is a "continue" button in this "second level" dialogue which has two consequences:
First one: Self.Destroy();
Second one: goes to step 2, i.e. destroys the "first level" dialogue and creates it again.
To that point the program seems to work fine, however, when I finish "adding" variables to the "first level" dialogue I Destroy it and then I cannot go back to the main Frame stated in step 1.
Why is this happening?
All the Dialogues are shown via ShowModal(). However if I use only Show() it seems to work fine but, since the program has many menus and items, ShowModal() is preferred.
Any idea why it works with Show() but not with ShowModal()?
If there is a simpler way to perform the task I want to do, it would be appreciated.
import wx
from wx.lib.pubsub import Publisher as pub
class itemReceiver(object):
def __init__(self):
pub.subscribe(self.__OnShowDialog, 'show.dialog')
def __OnShowDialog(self, message):
self.dlgParent = message.data[0]
print str(self.dlgParent)
self.valuesToShow = message.data[1]
print self.valuesToShow
#try to destroy dialog before creating a new one
try:
self.manageParametersDialog.Destroy()
except:
pass
self.manageParametersDialog = manageParamsDialog(self.dlgParent, self.valuesToShow)
print "ready to show first level dialogue"
self.manageParametersDialog.ShowModal() #if .Show() instead, there is no problem
class secondaryReceiver(object):
def __init__(self):
pub.subscribe(self.__OnShowDialog, 'add.item')
def __OnShowDialog(self, message):
dlgParent = message.data[0]
dlgGrandParent = message.data[1]
self.variableList = message.data[2]
editParameterDialog = editParamDlg(dlgParent, dlgGrandParent, self.variableList)
editParameterDialog.ShowModal()
class manageParamsDialog (wx.Dialog):
def __init__(self, parent, valueList):
self.valueList = valueList
self.parent = parent
wx.Dialog.__init__(self, parent, -1, "first level dialogue", style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
sizer=wx.BoxSizer(wx.VERTICAL)
self.optionList = wx.ListBox(self, -1, size=(200, 70), choices = valueList)
sizer.Add(self.optionList)
addButton = wx.Button(self, -1, 'Add New')
self.Bind(wx.EVT_BUTTON, self.OnButton, addButton)
sizer.Add(addButton)
cancelButton = wx.Button(self, -1, 'Cancel')
self.Bind(wx.EVT_BUTTON, self.OnCancel, cancelButton)
sizer.Add(cancelButton)
self.SetSizer(sizer)
self.Fit()
def OnButton (self, e):
pub.sendMessage('add.item', [self, self.parent, self.valueList])
def OnCancel(self,e):
self.Destroy()
class editParamDlg(wx.Dialog):
def __init__(self, parent, grandParent, variableList):
self.variableList = variableList
self.grandParent = grandParent
wx.Dialog.__init__(self, parent, -1, "second level dialogue", style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
hboxSizer = wx.BoxSizer(wx.HORIZONTAL)
self.textInput = wx.TextCtrl(self, -1)
hboxSizer.Add(self.textInput)
addButton = wx.Button(self, -1, 'Continue')
self.Bind(wx.EVT_BUTTON, self.OnAdd, addButton)
hboxSizer.Add(addButton)
cancelButton = wx.Button(self, -1, 'Cancel')
self.Bind(wx.EVT_BUTTON, self.OnCancel, cancelButton)
hboxSizer.Add(cancelButton)
self.SetSizer(hboxSizer)
self.Fit()
def OnAdd(self, e):
self.variableList.append(self.textInput.GetValue())
self.Destroy()
pub.sendMessage('show.dialog',[self.grandParent, self.variableList])
def OnCancel(self,e):
self.Destroy()
class ToolbarFrame(wx.Frame):
#this ToolbarFrame is the main window, with a Toolbar and a white panel below.
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, "this is a frame", size=(480, 320))
myPanel = wx.Panel(self)
myPanel.SetBackgroundColour("White")
menuBar = wx.MenuBar()
fileMenu = wx.Menu()
menuItem = wx.MenuItem(fileMenu, -1, "menu item", "opens dialog via pubsub")
self.Bind(wx.EVT_MENU, self.OnMenuItem, menuItem)
fileMenu.AppendItem(menuItem)
menuBar.Append(fileMenu, "File")
self.SetMenuBar(menuBar)
def OnMenuItem(self, e):
pub.sendMessage('show.dialog', [self, ["one", "two", "three"]])
app = wx.PySimpleApp()
frame = ToolbarFrame(parent=None, id=-1)
frame.Show()
newItemListener = itemReceiver()
editParameterListener = secondaryReceiver()
app.MainLoop()
try changing secondaryReciever as follows
class secondaryReceiver(object):
def __init__(self):
pub.subscribe(self.__OnShowDialog, 'add.item')
def __OnShowDialog(self, message):
dlgParent = message.data[0]
dlgGrandParent = message.data[1]
self.variableList = message.data[2]
editParameterDialog = editParamDlg(dlgParent, dlgGrandParent, self.variableList)
editParameterDialog.ShowModal()
#this line will not execute till the dialog closes
self.dlgParent.optionList.SetItems(editParameterDialog.variableList)
editParameterDialog.Destroy()
and also change editParamDlg
def OnAdd(self, e):
self.variableList.append(self.textInput.GetValue())
self.Close()
the problem was that you would call the show.modal from that OnAdd ... which would try to destroy the existing window and then open a new one... but the old one wasnt destroyed ... this left weird remnants that caused you errors ... and really all you want to do is update the item list ...
I am making a music player and everything goes well except when I click the next button, it loads the next song, but wont play it unless the user presses the play button. Is there anyway to make this play automatically just by hitting the next button and not having to press the play button afterwards. I have tried binding the play function and the load function to the button and that did not work, I have also tried just including mc.Play() in my load function, but that does not work either.
Here is my code:
import wx
import wx.media
import os
class MainWindow(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id,'Music Player',size=(900,670),style=wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.CAPTION|wx.CLOSE_BOX|wx.CLIP_CHILDREN)
wx.Frame.CenterOnScreen(self)
bg = wx.Image('bg.png', wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.bitmap1 = wx.StaticBitmap(self, -1, bg, (0,0))
panel = wx.Panel(self,style=wx.STAY_ON_TOP)
##MENU AND STATUS BAR
self.status = self.CreateStatusBar()
self.status.SetStatusText('Ready')
menubar = wx.MenuBar()
file_menu = wx.Menu()
view_menu = wx.Menu()
controls_menu = wx.Menu()
help_menu = wx.Menu()
#MENU ID'S
ID_FILE_LOAD = 2
ID_FILE_EXIT = 3
ID_VIEW_SHOW_STATUSBAR = 4
ID_CONTROLS_PLAY = 5
ID_CONTROLS_PAUSE = 6
ID_CONTROLS_STOP = 7
ID_HELP_ABOUT = 8
##FILE MENU
file_menu.Append(ID_FILE_LOAD, "&Load...\tCtrl+L", "This will let you choose a song to load")
file_menu.AppendSeparator()
file_menu.Append(ID_FILE_EXIT,"Exit","This will exit the program")
##VIEW MENU
self.check_statusbar = view_menu.Append(ID_VIEW_SHOW_STATUSBAR,'Show Stat&usbar\tCtrl+U', "This will disable the statusbar", kind=wx.ITEM_CHECK)
view_menu.Check(self.check_statusbar.GetId(), True)
##CONTROLS MENU
controls_menu.Append(ID_CONTROLS_PLAY,"&Play\tEnter", "Play the selected song")
controls_menu.Append(ID_CONTROLS_PAUSE,"&Pause\tSpace", "Pause the selected song")
##MENUBAR APPEND
menubar.Append(file_menu,"File")
menubar.Append(view_menu,"View")
menubar.Append(controls_menu,"Controls")
menubar.Append(help_menu,"Help")
self.SetMenuBar(menubar)
##MENU ACTION BINDING
self.Bind(wx.EVT_MENU, self.Load, None, 2)
self.Bind(wx.EVT_MENU, self.Close, None, 3)
self.Bind(wx.EVT_MENU, self.ToggleStatusBar, self.check_statusbar)
self.Bind(wx.EVT_MENU, self.Play, None, 5)
self.Bind(wx.EVT_MENU, self.Pause, None, 6)
self.Bind(wx.EVT_MENU, self.About, None, 8)
##FONTS
font1 = wx.Font(14, wx.DEFAULT, wx.NORMAL, wx.BOLD)
font2 = wx.Font(14, wx.DEFAULT, wx.NORMAL, wx.BOLD)
try:
self.mc = wx.media.MediaCtrl(self)
except NotImplementedError:
raise
##BUTTONS
bttnprt = self.bitmap1
loadButton = wx.Button(bttnprt, -1, "Load File...", pos=(308,435), size=(281,31))
self.Bind(wx.EVT_BUTTON, self.Load, loadButton)
playButton = wx.Button(bttnprt, -1, "Play", pos=(458,491), size=(57,57))
self.Bind(wx.EVT_BUTTON, self.Play, playButton)
pauseButton = wx.Button(bttnprt, -1, "Pause", pos=(383,491), size=(57,57))
self.Bind(wx.EVT_BUTTON, self.Pause, pauseButton)
volumeUpButton = wx.Button(bttnprt, -1, "Up", pos=(400,299), size=(95,52))
self.Bind(wx.EVT_BUTTON, self.onSetVolumeUp, volumeUpButton)
volumeDownButton = wx.Button(bttnprt, -1, "Down", pos=(400,363), size=(95,52))
self.Bind(wx.EVT_BUTTON, self.onSetVolumeDown, volumeDownButton)
backButton = wx.Button(bttnprt, -1, "Back", pos=(308,491), size=(57,57))
self.Bind(wx.EVT_BUTTON, self.previousSong, backButton)
nextButton = wx.Button(bttnprt, -1, "Next", pos=(533,491), size=(57,57))
self.Bind(wx.EVT_BUTTON, self.nextSong, nextButton)
self.volumeCtrl = wx.Slider(bttnprt, value=50, minValue=0, maxValue=100, style=wx.SL_VERTICAL|wx.SL_INVERSE, pos=(300,300))
# self.volumeCtrl.Bind(wx.EVT_SLIDER, self.onSetVolume)
self.volumeCtrl.Hide()
songlist = os.listdir('songs')
self.myListBox = listbox = wx.ListBox(bttnprt, -1, (301,80), (296,206), songlist, wx.LB_SINGLE)
self.Bind(wx.EVT_LISTBOX, self.selLoadFile, listbox)
# self.st_file = wx.StaticText(bttnprt, -1, "Blank", pos=(30,30))
def newWin(self, event):
self.new = NewWindow(parent=self, id=-1)
self.new.Show()
def Close(self, event):
box=wx.MessageDialog(None, 'Are you sure you want to exit?', 'Exit program?', wx.YES_NO)
answer=box.ShowModal()
if answer==wx.ID_YES:
self.Destroy()
def About(self, event):
self.new = AboutWindow(parent=self, id=-1)
self.new.Show()
def selLoadFile(self, event):
my_selection = self.myListBox.GetStringSelection()
file_path = os.path.join(os.getcwd(),"songs",my_selection)
self.doLoadFile2(file_path)
def Load(self, event):
dlg = wx.FileDialog(self, "Choose a media file", "songs", "", "*.*", wx.OPEN)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
self.doLoadFile(path)
dlg.Destroy()
def load2(self):
my_selection = self.myListBox.GetStringSelection()
file_path = os.path.join(os.getcwd(),"songs",my_selection)
self.doLoadFile2(file_path)
self.mc.Play()
def doLoadFile(self, path):
if not self.mc.Load(path):
wx.MessageBox("Unable to load %s: Unsupported format?" % path, "ERROR", wx.ICON_ERROR | wx.OK)
else:
folder, filename = os.path.split(path)
self.mc.SetBestFittingSize()
self.mc.Play()
def doLoadFile2(self, file_path):
if not self.mc.Load(file_path):
wx.MessageBox("Unable to load %s: Unsupported format?" % file_path, "ERROR", wx.ICON_ERROR | wx.OK)
else:
folder, filename = os.path.split(file_path)
#self.st_file.SetLabel('%s' % filename)
self.status.SetStatusText("Now Playing: " +'%s' % filename)
self.mc.SetBestFittingSize()
self.mc.Play()
def Play(self, event):
self.mc.Play()
def Pause(self, event):
self.mc.Pause()
def onSetVolumeUp(self, event):
self.currentVolume = self.volumeCtrl.GetValue()
self.newVolumeAdd = self.currentVolume + 1.5
self.volumeCtrl.SetValue(self.newVolumeAdd)
self.mc.SetVolume(float(self.currentVolume) / 100)
def onSetVolumeDown(self, event):
self.currentVolume = self.volumeCtrl.GetValue()
self.newVolumeSub = self.currentVolume - 1.5
self.volumeCtrl.SetValue(self.newVolumeSub)
self.mc.SetVolume(float(self.currentVolume) / 100)
def previousSong(self, event):
current = self.myListBox.GetSelection()
new = current - 1
self.myListBox.SetSelection(new)
self.mc.Stop()
self.load2()
def nextSong(self, event):
current = self.myListBox.GetSelection()
new = current + 1
self.myListBox.SetSelection(new)
self.mc.Stop()
self.load2()
def ToggleStatusBar(self, e):
if self.check_statusbar.IsChecked():
self.status.Show()
self.status.SetStatusText('Ready')
else:
self.status.Hide()
##RUN##
if __name__=='__main__':
app=wx.PySimpleApp()
frame=MainWindow(parent=None,id=-1)
frame.Show()
app.MainLoop()
If this is windows, and possibly specifically Win 7, 64bit, you need to do 2 things to get this to work.
As J.F. Sebastian mentioned, you need to call the play from a method that is bound to the EVT_MEDIA_LOADED event. The docs actually mention this.
Specify a backend when you create the MediaCtrl. I'm not sure why its needed, but it didn't work for me until I did that.
So try the following changes:
...
#change this existing line
self.mc = wx.media.MediaCtrl(self, szBackend=wx.media.MEDIABACKEND_WMP10)
...
# add this new line
self.Bind(wx.media.EVT_MEDIA_LOADED, self.song_is_loaded)
...
# add this new method
def song_is_loaded(self, event):
self.mc.Play()
You can also remove all the self.mc.Play() calls now except the one in the Play() function itself (since loading the file will now cause it to play).
I also noticed you've defined a bunch of ID_XXX constants, but then just used numbers when you bound your menu buttons.