I am trying to get a better understanding of how wxPython 'scans'.
Please see my code below:
import os
import wx
from time import sleep
NoFilesToCombine = 0
class PDFFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, size=(400,400))
panel = wx.Panel(self)
self.Show()
try: #Set values of PDFNoConfirmed to zero on 1st initialisation
if PDFNoConfimed != 0:
None
except UnboundLocalError:
PDFNoConfimed = 0
try: #Set values of PDFNoConfirmed to zero on 1st initialisation
if PDFNoConfirmedInitially != 0:
None
except UnboundLocalError:
PDFNoConfirmedInitially = 0
while ((PDFNoConfimed == 0) and (PDFNoConfirmedInitially == 0)):
while PDFNoConfirmedInitially == 0:
BoxInputNo = wx.NumberEntryDialog(panel, "So You Want To Combine PDF Files?", "How Many?", "Please Enter", 0, 2, 20)
if BoxInputNo.ShowModal() == wx.ID_OK: #NumberEntryDialog Pressed OK
NoFilesToCombine = BoxInputNo.GetValue()
PDFNoConfirmedInitially = 1
elif BoxInputNo.ShowModal() == wx.ID_CANCEL:
exit()
print(NoFilesToCombine)
ConfirmationLabel = wx.StaticText(panel, -1, label="You Have Selected " + str(NoFilesToCombine) + " Files To Combine, Is This Right?", pos=(20, 100))
ConfirmationBoxConfirm = wx.ToggleButton(panel, label="Confirm", pos=(20, 200))
ConfirmationBoxCancel = wx.ToggleButton(panel, label="Cancel", pos=(180, 200))
#if ConfirmationBoxConfirm.GetValue() == 1:
# exit()
if ConfirmationBoxCancel.GetValue() == 1:
PDFNoConfirmedInitially = 0
app = wx.App()
frame = PDFFrame(None, title="Robs PDF Combiner Application")
app.MainLoop()
Now this is a work in progress so it obviously isn't complete. However what I'm trying to accomplish with the above is:
Display a number entry popup. If user presses 'cancel' exit the application (this works but needs 2 presses for some reason?). If press OK, then:
Display the number entered in step 1, with 2 additional buttons. The 'confirm' doesn't do anything as yet, but the 'cancel' should take you back to step 1. (by resetting the PDFNoConfirmedInitially flag).
Now step 2 doesn't work. When I debug it almost appears as though the PDFFrameonly gets scanned once. My presumably false assumption being that this would be continually scanned due to app.MainLoop() continually scanning wx.App() which in turn would call the child frame?
Help/ pointers/ deeper understanding always appreciated,
Thanks
Rob
1) ShowModal() shows dialog window and you use it two times
if BoxInputNo.ShowModal() == wx.ID_OK:
and
elif BoxInputNo.ShowModal() == wx.ID_CANCEL:
so it shows your window two times.
And only at second time you check wx.ID_CANCEL.
You should run it only once and check its result
result = BoxInputNo.ShowModal()
if result == wx.ID_OK:
pass
elif result == wx.ID_CANCEL:
self.Close()
return
2) You have to assign function to button and this function should reset variable and show dialog window again. But I think wx.Button could be better then wx.ToggleButton
ConfirmationBoxCancel = wx.Button(panel, label="Cancel", pos=(180, 200))
ConfirmationBoxCancel.Bind(wx.EVT_BUTTON, self.on_button_cancel)
def on_button_cancel(self, event):
#print('event:', event)
pass
# reset variable and show dialog window
Frankly I don't understand some of your variables. Maybe if you use True/False instead of 0/1 then they will be more readable. But main problem for me are while loops. GUI frameworks (wxPython, tkinter, PyQt, etc) should run only one loop - Mainloop(). Any other loop may block Mainloop() and GUI will freeze.
I created own version without any while loop but I don't know if it resolve all problems
import wx
class PDFFrame(wx.Frame):
def __init__(self, parent, title):
super().__init__(parent, -1, title, size=(400,400))
self.panel = wx.Panel(self)
self.Show()
# show dialog at start
if self.show_dialog(self.panel):
# create StaticLabel and buttons
self.show_confirmation(self.panel)
else:
# close main window and program
self.Close()
def show_dialog(self, panel):
"""show dialog window"""
global no_files_to_combine
box_input_no = wx.NumberEntryDialog(panel, "So You Want To Combine PDF Files?", "How Many?", "Please Enter", 0, 2, 20)
result = box_input_no.ShowModal()
if result == wx.ID_OK: #NumberEntryDialog Pressed OK
no_files_to_combine = box_input_no.GetValue()
return True
elif result == wx.ID_CANCEL:
print('exit')
return False
def show_confirmation(self, panel):
"""create StaticLabel and buttons"""
self.confirmation_label = wx.StaticText(self.panel, -1, label="You Have Selected {} Files To Combine, Is This Right?".format(no_files_to_combine), pos=(20, 100))
self.confirmation_box_confirm = wx.Button(self.panel, label="Confirm", pos=(20, 200))
self.confirmation_box_cancel = wx.Button(self.panel, label="Cancel", pos=(180, 200))
# assign function
self.confirmation_box_confirm.Bind(wx.EVT_BUTTON, self.on_button_confirm)
self.confirmation_box_cancel.Bind(wx.EVT_BUTTON, self.on_button_cancel)
def update_confirmation(self):
"""update existing StaticLabel"""
self.confirmation_label.SetLabel("You Have Selected {} Files To Combine, Is This Right?".format(no_files_to_combine))
def on_button_cancel(self, event):
"""run when pressed `Cancel` button"""
#print('event:', event)
# without `GetValue()`
if self.show_dialog(self.panel):
# update existing StaticLabel
self.update_confirmation()
else:
# close main window and program
self.Close()
def on_button_confirm(self, event):
"""run when pressed `Confirn` button"""
#print('event:', event)
# close main window and program
self.Close()
# --- main ---
no_files_to_combine = 0
app = wx.App()
frame = PDFFrame(None, title="Robs PDF Combiner Application")
app.MainLoop()
Related
I want to switch pages with the help of buttons in Gtk.Stack. There are 3 pages, and the title bar of the application has one forward and one back button. I want it to go to the next page when the forward button is pressed, and to go to the previous page when the back button is pressed. Its current state can only switch between page 1 and page 2.
import gi, os
gi.require_version("Gtk", "3.0")
gi.require_version("Handy", "1")
from gi.repository import Gtk, Handy
Handy.init()
class MyWindow(Handy.Window):
def __init__(self):
super().__init__(title="Hello World")
self.set_default_size(500, 300)
# WindowHandle
self.handle = Handy.WindowHandle()
self.add(self.handle)
# Box
self.winbox = Gtk.Box(spacing=6, orientation=Gtk.Orientation.VERTICAL)
self.handle.add(self.winbox)
# Headerbar
self.hb = Handy.HeaderBar()
self.hb.set_show_close_button(True)
self.hb.props.title = "Stack Example"
self.winbox.pack_start(self.hb, False, True, 0)
# Stack
self.stack = Gtk.Stack()
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
self.winbox.pack_start(self.stack, True, True, 0)
# Labels
self.label = Gtk.Label(label="Page 1")
self.stack.add_titled(self.label, "page0", "Label")
self.label = Gtk.Label(label="Page 2")
self.stack.add_titled(self.label, "page1", "Label")
self.label = Gtk.Label(label="Page 3")
self.stack.add_titled(self.label, "page2", "Label")
# Headerbar button 1
self.button = Gtk.Button()
self.button = Gtk.Button.new_from_icon_name("pan-start-symbolic", Gtk.IconSize.MENU)
self.hb.pack_start(self.button)
self.button.connect('clicked', self.on_button1_clicked)
# Headerbar button 2
self.button2 = Gtk.Button()
self.button2 = Gtk.Button.new_from_icon_name("pan-end-symbolic", Gtk.IconSize.MENU)
self.hb.pack_start(self.button2)
self.button2.connect("clicked", self.on_button2_clicked)
def on_button1_clicked(self, widget):
self.stack.set_visible_child_name("page1")
def on_button2_clicked(self, widget):
self.stack.set_visible_child_name("page2")
win = MyWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
I don't know if there's an easy way to make visible the next child in a GtkStack or if another container has this functionality apart from GtkAssistant.
Nevertheless there's multiple ways you can implement this yourself. Either like so:
def on_button1_clicked(self, widget):
pages = self.stack.get_children()
cur_page = self.stack.get_visible_child()
i = pages.index(cur_page)
if i == 0: return
self.stack.set_visible_child(pages[i-1])
def on_button2_clicked(self, widget):
pages = self.stack.get_children()
cur_page = self.stack.get_visible_child()
i = pages.index(cur_page)
if i == len(pages) - 1: return
self.stack.set_visible_child(pages[i+1])
where you get the stack's children with GtkContainer.get_children(), find the index of the current visible child and then plus/minus one to get the next/prev page.
Caveat: I'm not sure if get_children() always returns the child widgets in the order they are added.
Alternatively in your __init__() function, you can create a list to store your page names/widgets e.g. self.page_names = ['page0', 'page1', 'page2']. And then you can do:
def on_button1_clicked(self, widget):
cur_page_name = self.stack.get_visible_child_name()
i = self.page_names.index(cur_page_name)
if i == 0: return
self.stack.set_visible_child_name(self.page_names[i-1])
Or maybe you extract the page number from the child name (e.g. 0 from page0) using RegEx and generate the next page's name. There's many ways to accomplish this, I personally would keep a variable of all the pages and use that to determine which page is next/prev.
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.
When I use keyboard for dropdown selection, scroll doesn't move up and down,so I am not able to see which one is selected right now. How to move scroll up and down when keyboard is pressed up and down. My sample code is as following:
import PySimpleGUI as sg
class GUI():
def __init__(self):
self.data = [(10), (20), (30), (40), (50), (60), (70), (80), (90), (100)]
self.work_order_currrent_selection_index = 0
def run(self):
layout = [[sg.Listbox(values=self.data, size=(35, 3), enable_events=True, key='selected_key')]]
# Create the Window
self.testWindow = sg.Window('Test', return_keyboard_events=True).Layout(layout).Finalize()
self.testWindow.Maximize()
self.testWindow.Element('selected_key').Update(set_to_index=0)
# Event Loop to process "events"
while True:
event, values = self.testWindow.Read()
if event in('Up:111', '16777235'):
if(hasattr(self, 'testWindow')):
self.work_order_currrent_selection_index = (self.work_order_currrent_selection_index - 1) % len(self.data)
self.testWindow.Element('selected_key').Update(set_to_index=self.work_order_currrent_selection_index)
elif event in ('Down:116',' 16777237'):
if(hasattr(self, 'testWindow')):
self.work_order_currrent_selection_index = (self.work_order_currrent_selection_index + 1) % len(self.data)
self.testWindow.Element('selected_key').Update(set_to_index=self.work_order_currrent_selection_index)
self.testWindow.Close()
if __name__ == '__main__':
app = GUI()
app.run()
When application launch first time I just able to see three dropdown as,
I pressed down arrow key then selection go down one by one like that,
But after selection of 30, on the down key press selection move to next one like 40, 50.. except scrolling, so I am not able to see which one is selected now.
Is there any way to move selection along with scrolling?
See the fourth image, here selection moved to 40 but scroll not moved down. Same issue with up key pressed.
Maybe this will get you a little closer to what you're looking for
import PySimpleGUI as sg
class GUI():
def __init__(self):
self.data = [(10), (20), (30), (40), (50), (60), (70), (80), (90), (100)]
self.work_order_currrent_selection_index = 0
def run(self):
layout = [[sg.Listbox(values=self.data, size=(35, 3), enable_events=True, key='selected_key')]]
# Create the Window
self.testWindow = sg.Window('Test', layout, return_keyboard_events=True, finalize=True)
# self.testWindow.Maximize()
self.testWindow.Element('selected_key').Update(set_to_index=0)
# Event Loop to process "events"
while True:
event, values = self.testWindow.Read()
if event is None:
break
if event.startswith('Up'):
self.work_order_currrent_selection_index = (self.work_order_currrent_selection_index - 1) % len(self.data)
self.testWindow['selected_key'].Update(set_to_index=self.work_order_currrent_selection_index,scroll_to_index=self.work_order_currrent_selection_index )
elif event.startswith('Down'):
self.work_order_currrent_selection_index = (self.work_order_currrent_selection_index + 1) % len(self.data)
self.testWindow['selected_key'].Update(set_to_index=self.work_order_currrent_selection_index, scroll_to_index=self.work_order_currrent_selection_index)
self.testWindow.Close()
if __name__ == '__main__':
app = GUI()
app.run()
I'm struggling to find a way to use function from within a wxPython event handler function. Say I have a button that when clicked it runs a function called OnRun using an event handler. However, the user forgot to click a RadionButton before the OnRun button and I want to pop-up a MessageDialog telling them they forgot a step. I'm going to reuse this MessageDialog several times, thus rather than doing a copy/paste of the same code I would like to just have this MessageDialog in a function and call this MessageDialog function if the user forgets to check a RadioButton.
If this wasn't a function used in an Event Handler I know I could simply put the function as an argument but I'm not seeing a way I can do this with these. Any help here would be appreciated.
The following code shows how to create a little method that you can reuse to show custom dialogs and tells the user that they need to accept the agreement. You can change the conditionals to do whatever you want, of course. And you can change the "showMsg" method so that the icon changes too with just a little tweaking.
import wx
########################################################################
class TestFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Test")
panel = wx.Panel(self)
self.radios = wx.RadioBox(panel, label="Choices",
choices = ["None", "Accept", "Reject"])
button = wx.Button(panel, label="Run")
button.Bind(wx.EVT_BUTTON, self.onBtn)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.radios, 0, wx.ALL, 5)
sizer.Add(button, 0, wx.ALL, 5)
panel.SetSizer(sizer)
#----------------------------------------------------------------------
def onBtn(self, event):
""""""
btn = event.GetEventObject()
btn.SetLabel("Running")
radio_value = self.radios.GetStringSelection()
if radio_value == "None":
self.showMsg("Error", "Please Choose 'Accept' or 'Reject'!")
elif radio_value == "Accept":
self.showMsg("Message", "Thank you for accepting!")
else:
self.showMsg("Message", "We're sorry, but you cannot continue the install")
#----------------------------------------------------------------------
def showMsg(self, title, msg):
""""""
dlg = wx.MessageDialog(None, msg, title, wx.OK | wx.ICON_QUESTION)
dlg.ShowModal()
dlg.Destroy()
if __name__ == "__main__":
app = wx.App(False)
frame = TestFrame()
frame.Show()
app.MainLoop()
I will make a stab at this, even if the answer seems too direct. I would set a property in the enclosing frame that flags whether the Radio Button has been clicked or not. Then when OnRun is called check that property. Should it be in the wrong state, call the MessageDialog and abort/pause/modify the OnRun.
EDIT Here is what I mean, a trivial example with two buttons, neither of which will lead to further action unless a user agreement is clicked.
import wx
class ButtonFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Button Example',
size=(300, 100))
panel = wx.Panel(self, -1)
self.radio = wx.RadioButton(panel, -1, "Accept user agreement", pos=(50, 10))
self.button = wx.Button(panel, -1, "Run", pos=(50, 30))
self.Bind(wx.EVT_BUTTON, self.OnRun, self.button)
self.button.SetDefault()
self.btn2 = wx.Button(panel, -1, "Walk", pos=(150, 30))
self.Bind(wx.EVT_BUTTON, self.OnWalk, self.btn2)
def OnRun(self, event):
if not self.CheckRadio():
return
self.button.SetLabel("Running")
def OnWalk(self, event):
if not self.CheckRadio():
return
self.btn2.SetLabel("Walking")
def CheckRadio(self):
accepted = self.radio.GetValue()
if not accepted:
dlg = wx.MessageDialog(None, 'First accept the user agreement',
'MessageDialog', wx.OK | wx.ICON_QUESTION)
result = dlg.ShowModal() # result not used in this demo
dlg.Destroy()
return False
else:
return True
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = ButtonFrame()
frame.Show()
app.MainLoop()
Code is adapted from Listing 7.11 of wxPython in Action. I hope this helps, if you have not already solved this n the time that has passed.
You can create your own MessageDialog (inheriting), or you can use functools.partial/lambda to pass an additional argument to the event handler:
self.Bind(wx.MY_EVENT, lambda evt: self.OnEventX(evt, handler=foo), id=12)
I have been working on this project for some time now - it was originally supposed to be a test to see if, using wxPython, I could build a button 'from scratch.' From scratch means: that i would have full control over all the aspects of the button (i.e. controlling the BMP's that are displayed... what the event handlers did... etc.)
I have run into several problems (as this is my first major python project.) Now, when the all the code is working for the life of me I can't get an image to display.
Basic code - not working
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(wx.Bitmap("/home/wallter/Desktop/Mouseover.bmp"), 100, 100)
self.Refresh()
self.Update()
Full Main.py
import wx
from Custom_Button import Custom_Button
from wxPython.wx import *
ID_ABOUT = 101
ID_EXIT = 102
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wxFrame.__init__(self, parent, ID, title,
wxDefaultPosition, wxSize(400, 400))
self.CreateStatusBar()
self.SetStatusText("Program testing custom button overlays")
menu = wxMenu()
menu.Append(ID_ABOUT, "&About", "More information about this program")
menu.AppendSeparator()
menu.Append(ID_EXIT, "E&xit", "Terminate the program")
menuBar = wxMenuBar()
menuBar.Append(menu, "&File");
self.SetMenuBar(menuBar)
# The call for the 'Experiential button'
self.Button1 = Custom_Button(parent, -1,
wx.Point(100, 100),
wx.Bitmap("/home/wallter/Desktop/Mouseover.bmp"),
wx.Bitmap("/home/wallter/Desktop/Normal.bmp"),
wx.Bitmap("/home/wallter/Desktop/Click.bmp"))
# The following three lines of code are in place to try to get the
# Button1 to display (trying to trigger the Paint event (the _onPaint.)
# Because that is where the 'draw' functions are.
self.Button1.Show(true)
self.Refresh()
self.Update()
# Because the Above three lines of code did not work, I added the
# following four lines to trigger the 'draw' functions to test if the
# '_onPaint' method actually worked.
# These lines do not work.
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.DrawBitmap(wx.Bitmap("/home/wallter/Desktop/Mouseover.bmp"), 100, 100)
EVT_MENU(self, ID_ABOUT, self.OnAbout)
EVT_MENU(self, ID_EXIT, self.TimeToQuit)
def OnAbout(self, event):
dlg = wxMessageDialog(self, "Testing the functions of custom "
"buttons using pyDev and wxPython",
"About", wxOK | wxICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def TimeToQuit(self, event):
self.Close(true)
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(NULL, -1, "wxPython | Buttons")
frame.Show(true)
self.SetTopWindow(frame)
return true
app = MyApp(0)
app.MainLoop()
Full CustomButton.py
import wx
from wxPython.wx import *
class Custom_Button(wx.PyControl):
def __init__(self, parent, id, Pos, Over_BMP, Norm_BMP, Push_BMP, **kwargs):
wx.PyControl.__init__(self,parent, id, **kwargs)
self.Bind(wx.EVT_LEFT_DOWN, self._onMouseDown)
self.Bind(wx.EVT_LEFT_UP, self._onMouseUp)
self.Bind(wx.EVT_LEAVE_WINDOW, self._onMouseLeave)
self.Bind(wx.EVT_ENTER_WINDOW, self._onMouseEnter)
self.Bind(wx.EVT_ERASE_BACKGROUND,self._onEraseBackground)
self.Bind(wx.EVT_PAINT,self._onPaint)
self.pos = Pos
self.Over_bmp = Over_BMP
self.Norm_bmp = Norm_BMP
self.Push_bmp = Push_BMP
self._mouseIn = False
self._mouseDown = False
def _onMouseEnter(self, event):
self._mouseIn = True
def _onMouseLeave(self, event):
self._mouseIn = False
def _onMouseDown(self, event):
self._mouseDown = True
def _onMouseUp(self, event):
self._mouseDown = False
self.sendButtonEvent()
def sendButtonEvent(self):
event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
event.SetInt(0)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
def _onEraseBackground(self,event):
# reduce flicker
pass
def Iz(self):
dc = wx.BufferedPaintDC(self)
dc.DrawBitmap(self.Norm_bmp, 100, 100)
def _onPaint(self, event):
# The printing functions, they should work... but don't.
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(self.Norm_bmp)
# This never printed... I don't know if that means if the EVT
# is triggering or what.
print '_onPaint'
# draw whatever you want to draw
# draw glossy bitmaps e.g. dc.DrawBitmap
if self._mouseIn: # If the Mouse is over the button
dc.DrawBitmap(self.Over_bmp, self.pos)
else: # Since the mouse isn't over it Print the normal one
# This is adding on the above code to draw the bmp
# in an attempt to get the bmp to display; to no avail.
dc.DrawBitmap(self.Norm_bmp, self.pos)
if self._mouseDown: # If the Mouse clicks the button
dc.DrawBitmap(self.Push_bmp, self.pos)
This code won't work? I get no BMP displayed why? How do i get one? I've gotten the staticBitmap(...) to display one, but it won't move, resize, or anything for that matter... - it's only in the top left corner of the frame?
Note: the frame is 400pxl X 400pxl - and the "/home/wallter/Desktop/Mouseover.bmp"
Are your sure you code is working without exceptions because when I run it i get many errors, read the points below and you should have a button which at least draws correctly
When O run it it gives error because Custom_Button is passed NULL parent instead pass frame e.g. Custom_Button(self, ...)
Your drawBitmap call is also wrong, it throws exception, instead of dc.DrawBitmap(self.Norm_bmp) it should be dc.DrawBitmap(self.Norm_bmp, 0, 0)
dc.DrawBitmap(self.Over_bmp, self.pos) also throws error as pos should be x,y not a tuple so instead do dc.DrawBitmap(self.Over_bmp, *self.pos)
and lastly you do not need to do "from wxPython.wx import *" instead just do "from wx import *" and instead of wxXXX class names use wx.XXX, instead of true use True etc
here is my working code
from wx import *
ID_ABOUT = 101
ID_EXIT = 102
class Custom_Button(wx.PyControl):
def __init__(self, parent, id, Pos, Over_BMP, Norm_BMP, Push_BMP, **kwargs):
wx.PyControl.__init__(self,parent, id, **kwargs)
self.Bind(wx.EVT_LEFT_DOWN, self._onMouseDown)
self.Bind(wx.EVT_LEFT_UP, self._onMouseUp)
self.Bind(wx.EVT_LEAVE_WINDOW, self._onMouseLeave)
self.Bind(wx.EVT_ENTER_WINDOW, self._onMouseEnter)
self.Bind(wx.EVT_ERASE_BACKGROUND,self._onEraseBackground)
self.Bind(wx.EVT_PAINT,self._onPaint)
self.pos = Pos
self.Over_bmp = Over_BMP
self.Norm_bmp = Norm_BMP
self.Push_bmp = Push_BMP
self._mouseIn = False
self._mouseDown = False
def _onMouseEnter(self, event):
self._mouseIn = True
def _onMouseLeave(self, event):
self._mouseIn = False
def _onMouseDown(self, event):
self._mouseDown = True
def _onMouseUp(self, event):
self._mouseDown = False
self.sendButtonEvent()
def sendButtonEvent(self):
event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
event.SetInt(0)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
def _onEraseBackground(self,event):
# reduce flicker
pass
def Iz(self):
dc = wx.BufferedPaintDC(self)
dc.DrawBitmap(self.Norm_bmp, 100, 100)
def _onPaint(self, event):
# The printing functions, they should work... but don't.
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(self.Norm_bmp, 0, 0)
# This never printed... I don't know if that means if the EVT
# is triggering or what.
print '_onPaint'
# draw whatever you want to draw
# draw glossy bitmaps e.g. dc.DrawBitmap
if self._mouseIn: # If the Mouse is over the button
dc.DrawBitmap(self.Over_bmp, *self.pos)
else: # Since the mouse isn't over it Print the normal one
# This is adding on the above code to draw the bmp
# in an attempt to get the bmp to display; to no avail.
dc.DrawBitmap(self.Norm_bmp, *self.pos)
if self._mouseDown: # If the Mouse clicks the button
dc.DrawBitmap(self.Push_bmp, *self.pos)
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wx.Frame.__init__(self, parent, ID, title,
wx.DefaultPosition, wx.Size(400, 400))
self.CreateStatusBar()
self.SetStatusText("Program testing custom button overlays")
menu = wx.Menu()
menu.Append(ID_ABOUT, "&About", "More information about this program")
menu.AppendSeparator()
menu.Append(ID_EXIT, "E&xit", "Terminate the program")
menuBar = wx.MenuBar()
menuBar.Append(menu, "&File");
self.SetMenuBar(menuBar)
# The call for the 'Experiential button'
s = r"D:\virtual_pc\mockup\mockupscreens\embed_images\toolbar\options.png"
self.Button1 = Custom_Button(self, -1,
wx.Point(100, 100),
wx.Bitmap(s),
wx.Bitmap(s),
wx.Bitmap(s))
self.Button1.Show(True)
EVT_MENU(self, ID_ABOUT, self.OnAbout)
EVT_MENU(self, ID_EXIT, self.TimeToQuit)
def OnAbout(self, event):
dlg = wxMessageDialog(self, "Testing the functions of custom "
"buttons using pyDev and wxPython",
"About", wxOK | wxICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def TimeToQuit(self, event):
self.Close(true)
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, "wxPython | Buttons")
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()