I have been looking around the web since early morning and I can't seem to figure out how to get wxPython to show a dialogue box on my main frame.
import wx
class MainWindow(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id,'Window',size=(400,300))
panel = wx.Panel(self)
test = wx.TextEntryDialog(panel, 'Enter your word:',"New word","",
style=wx.OK|wx.CANCEL|wx.CENTRE,pos=(100,200))
def main():
pass
if __name__ == '__main__':
app = wx.App()
frame=MainWindow(parent=None,id=1)
frame.Show()
app.MainLoop()
It just opens a window without a text dialogue.
Use:
Dlg = wx.TextEntryDialog(panel, 'Enter your word:',"New word","",
style=wx.OK|wx.CANCEL|wx.CENTRE,pos=(100,200))
if Dlg.ShowModal() == wx.OK:
test = Dlg.GetValue()
del Dlg
As wx.TextEntryDialog is a dialogue class not one of the convenience dialogue functions you need to show it and get the value rather than just getting a reply.
Related
import wx
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Test")
panel = wx.Panel(self, wx.ID_ANY)
#Button is created; binded to onButton
button = wx.Button(panel, id=wx.ID_ANY, label="Press Me")
button.Bind(wx.EVT_BUTTON, self.onButton)
def onButton(self,EVT_BUTTON):
print("Hello world!")
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
#Runs Button command on startup
MyForm.onButton()
I want onButton() to run at startup, and have it be able to run when the
wx.Button is pressed. Unfortunetly, it comes up with this error:
>TypeError: onButton() missing 2 required positional arguments: 'self' and 'EVT_BUTTON'
It is slightly more difficult. I am guessing you are a beginner to programming. If so, I suggest, you learn some more basics. Doing GUI applications is a bit more advanced topic.
So, firstly, for your wxPython program to run, you must start an event loop, so your program should have something like this:
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
You defined your function onButton with 2 parameters. Therefore you must supply them when calling the function. The first one is instead of the self, and that is the frame. The second is named EVT_BUTTON (and giving the variable this name suggests that you actually do not understand these concepts, and that's the reason why I suggested that you start with studying basics).
So you could call
frame.OnButton(None)
before calling app.MainLoop() and the code will run. But that's probably not enough.
I'm running wxPython 4.0.1 msw (phoenix) with Python 3.6.5 on a Windows7 machine, as well as wxPython 2.9.4 with Python 2.7.
I'm observing an issue with a modal dialog, which doesn't block the access to its parent window behind. This only occurs if I run a progress dialog followed by a modal dialog. This behavior is somehow related to custom dialogs. Integrated dialogs like wx.MessageDialog doesn't seem to have this issue.
To isolate the issue, I've written an example. The first two buttons open either the progress or the modal dialog and work properly. The third button opens both dialogs in sequence. In this case the modal functionality of the custom dialog doesn't work and I'm able to access and close the mainframe. Which causes multiple issues.
Dialog is not modal, the main window can be accessed and closed
import wx
class SomeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, title='SomeDialog',
style=wx.DEFAULT_DIALOG_STYLE)
self.button_ok = wx.Button(self, wx.ID_OK, size=(120,-1))
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(self.button_ok, 0, wx.ALL|wx.ALIGN_CENTER, 10)
self.SetSizer(hsizer)
self.SetSize(self.BestSize)
self.Layout()
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, size=(400, 400))
self.button_progress = wx.Button(self, -1, 'Show Progress')
self.button_modal = wx.Button(self, -1, 'Show Modal')
self.button_both = wx.Button(self, -1, 'Show Both')
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_progress)
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_modal)
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_both)
sizer = wx.BoxSizer()
sizer.Add(self.button_progress)
sizer.Add(self.button_modal)
sizer.Add(self.button_both)
self.SetSizer(sizer)
def on_button(self, event):
if event.EventObject is self.button_progress:
self._show_progress_dialog()
elif event.EventObject is self.button_modal:
self._show_modal_dialog()
else:
self._show_progress_dialog()
self._show_modal_dialog()
def _show_progress_dialog(self):
max = 10
dlg = wx.ProgressDialog('Progress dialog example', 'Some message',
maximum=max, parent=self,
style=wx.PD_APP_MODAL|wx.PD_AUTO_HIDE)
keepGoing = True
count = 0
while keepGoing and count < max:
count += 1
wx.MilliSleep(250)
wx.Yield()
(keepGoing, skip) = dlg.Update(count)
dlg.Destroy()
def _show_modal_dialog(self):
with SomeDialog(self) as dlg:
dlg.CenterOnParent()
dlg.ShowModal()
if __name__ == '__main__':
app = wx.App()
frame = TestFrame()
frame.Show()
app.MainLoop()
In case this is an issue in the wxpython framework and not an issue with my implementation, it would be great if someone could provide me a workaround to show such dialogs in sequence.
This looks like a bug to me. I'm not sure why its happening, but one workaround would be to use wx.CallLater
changing _show_modal_dialog to:
def _show_modal_dialog(self):
def _make_dialog():
with SomeDialog(self) as dlg:
dlg.CenterOnParent()
dlg.ShowModal()
wx.CallLater(50, _make_dialog) # 50 mils is arbitrary
Seems to resolve the issue of the dialog not acting modalish. The problem with this workaround is that it will be non-blocking, meaning that any code that has to wait for the dialog to return has to be moved into the dialog class or passed to the dialog as a callback.
In the meantime I found a workaround myself which I like to share.
I added a handler catching the windows close event.
class TestFrame(wx.Frame):
def __init__(self):
#...
self.Bind(wx.EVT_CLOSE, self.on_close)
This event function checks if some child dialog is open and modal and performs a veto.
def on_close(self, event):
# In case any modal dialog is open, prevent the frame from closing.
for children in (c for c in self.Children if isinstance(c, wx.Dialog)):
if children.IsModal():
event.Veto()
return
event.Skip()
This is also only a workaround, but I seems to work for my use cases.
I'm building a menu for a frame with wxPython (python3).
I want to separate the main.py from the menus.py - have these two separated files so the code is organized into smaller pieces.
I need to be able to pass control back to the main.py from menus.py.
In particular, I need that the handlers for the events I bind for menu items (in menus.py) will reside in main.py, or, alternatively, have the handlers in menus.py, but reference objects in main.py (for example, to Close() the application for the "Exit" menu item.
This is what I have so far, and I tried both ways with no success. How is this achieved?
main.py
import wx
import menus
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "My App Title", size=(1200, 800))
self.panel = wx.Panel(self, wx.ID_ANY)
mainMenu = menus.MainMenu()
mainMenu.build_main_menu(self)
def onExit(self):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()
menus.py
from wx import Menu, MenuBar, MenuEvent, MenuItem, Frame, EVT_MENU
class MainMenu(object):
def build_main_menu(self, frame):
self.fileMenu = Menu()
exitMenuItem = self.fileMenu.Append(101, "E&xit", "Close the application")
menuBar = MenuBar()
menuBar.Append(self.fileMenu, "&File")
frame.Bind(EVT_MENU, MainFrame.onExit(frame), exitMenuItem)
frame.SetMenuBar(menuBar)
return self
You are really close, but just not quite there. For this sort of thing, there is no need to wrap the menu creation code inside a class, so I changed that to just a function. Here's menu.py:
from wx import Menu, MenuBar, MenuEvent, MenuItem, Frame, EVT_MENU
def build_main_menu(frame):
fileMenu = Menu()
exitMenuItem = fileMenu.Append(101, "E&xit", "Close the application")
menuBar = MenuBar()
menuBar.Append(fileMenu, "&File")
frame.Bind(EVT_MENU, frame.onExit, exitMenuItem)
frame.SetMenuBar(menuBar)
Note that you need to call the onExit using the frame instance, not by trying to call it via a class (MainFrame) that you haven't even imported. Also you do not call an event handler in the bind operation. It will get called when the button is pressed.
Next I updated your main.py:
import wx
import menus
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "My App Title", size=(1200, 800))
self.panel = wx.Panel(self, wx.ID_ANY)
mainMenu = menus.build_main_menu(self)
def onExit(self, event):
self.Close()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()
There are two changes here. First we don't need to create an instance of the menu module's class as there is no class any longer. Secondly event handlers take two arguments: self and event.
Now it works!
Ok, so I am playing around and trying to get an understanding of how to make GUI's with buttons, I figured i would start simple and make one with two buttons that displays a different message depending on which one is clicked. I made the first button and tested it... worked fine, made the second button and when i tested it i get the message for the second button when i click the first button, and nothing when i click the second button. I tried searching around but it doesn't seem anyone else had this issue so I am obviously doing something wrong.
#!/usr/bin/env python
import os
import wx
class Frame(wx.Frame):
def OnOpen(self,e):
self.dirname=''
dlg=wx.FileDialog(self,'Choose a File',self.dirname,'','*.*',wx.OPEN)
if dlg.ShowModal()==wx.OK:
self.filename=dlg.GetFileName()
self.dirname=dlg.GetDirectory()
f=open(os.path.join(self.dirname,self.filename),'r')
self.Control.SetValue(f.read())
f.close()
dlg.Destroy()
def OnAbout(self,e):
dlg=wx.MessageDialog(self,'Aoxx','Author',wx.OK)
dlg.ShowModal()
dlg.Destroy()
def OnExit(self,e):
dlg=wx.MessageDialog(self,'Exit','Terminate',wx.OK)
dlg.ShowModal()
self.Close(True)
dlg.Destroy()
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id,'Frame works',size=(450,600))
panel=wx.Panel(self)
self.CreateStatusBar()
filemenu=wx.Menu()
self.filemenu=wx.Menu()
menubar=wx.MenuBar()
menubar.Append(filemenu,'&File')
#menubar.Append(filemenu,'&Help')
self.SetMenuBar(menubar)
MenuOpen=filemenu.Append(wx.ID_OPEN,'&Open','File Dir')
MenuExit=filemenu.Append(wx.ID_ANY,'E&xit','Term')
MenuAbout=filemenu.Append(wx.ID_ABOUT,'&About','Info')
self.Bind(wx.EVT_MENU,self.OnOpen,MenuOpen)
self.Bind(wx.EVT_MENU,self.OnExit,MenuExit)
self.Bind(wx.EVT_MENU,self.OnAbout,MenuAbout)
pic1=wx.Image('C:\Users\******\Pictures\Tri.bmp', wx.BITMAP_TYPE_BMP).ConvertToBitmap()
self.button=wx.BitmapButton(panel,-1, pic1,pos=(10,10))
self.Bind(wx.EVT_BUTTON,self.ClickTri,self.button)
self.button.SetDefault()
pic2=wx.Image('C:\Users\******\Pictures\ClickWin.bmp', wx.BITMAP_TYPE_BMP).ConvertToBitmap()
self.buton=wx.BitmapButton(panel,-1,pic2,pos=(220,10))
self.Bind(wx.EVT_BUTTON,self.ClickWin,self.button)
def ClickTri(self,event):
dlg=wx.MessageDialog(self,'No touching the TriForce Rook!','HEY!!!',wx.OK)
dlg.ShowModal()
dlg.Destroy()
def ClickWin(self,event):
dlg=wx.MessageDialog(self,'You would.....','REALLY?',wx.OK)
dlg.ShowModal()
dlg.Destroy()
self.Show(True)
if __name__=='__main__':
app=wx.PySimpleApp()
frame=Frame(None,id=-1)
frame.Show()
app.MainLoop()
you cant have 2 self.button make the second one self.button2 or something
I'm assuming this is possible with a multiline text box, but not sure how to do it. What I'm looking to do is make a log box in my wxPython program, where I can write messages to it when certain actions happen. Also, i need to write the messages not only when an event happens, but certain times in the code. How would i get it to redraw the window so the messages appear at that instant?
I wrote an article on this sort of thing a couple years ago:
http://www.blog.pythonlibrary.org/2009/01/01/wxpython-redirecting-stdout-stderr/
If you would like just a log dialog in wxpython, use wx.LogWindow:
import wx
class MainWindow(wx.Frame):
def __init__(self, parent=None):
wx.Frame.__init__(self, parent, wx.NewId(), 'Logging')
self.log_window = wx.LogWindow(self, 'Log Window', bShow=True)
box_sizer = wx.BoxSizer(orient=wx.VERTICAL)
show_log_button = wx.Button(self, wx.NewId(), 'Show Log')
show_log_button.Bind(wx.EVT_BUTTON, self._show_log)
log_message_button = wx.Button(self, wx.NewId(), 'Log Message')
log_message_button.Bind(wx.EVT_BUTTON, self._log_message)
box_sizer.AddMany((show_log_button, log_message_button))
self.SetSizer(box_sizer)
self.Fit()
self.Bind(wx.EVT_CLOSE, self._on_close)
def _show_log(self, event):
self.log_window.Show()
def _log_message(self, event):
wx.LogError('New error message')
def _on_close(self, event):
self.log_window.this.disown()
wx.Log.SetActiveTarget(None)
event.Skip()
if __name__ == '__main__':
app = wx.PySimpleApp()
dlg = MainWindow()
dlg.Show()
app.MainLoop()
Where bShow in wx.LogWindow is if it's initially shown or not. This will log nicely all your wx.LogX messages that you can trigger, and it still passes it on to any other handlers.
Another method you could use would be to log with python and then, upon opening a frame/window with a text control in it, use LoadFile to open the log file:
import logging
LOG_FILENAME = 'example.log'
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)
logging.debug('This message should go to the log file')
Then, when creating a wx.TextCtrl somewhere:
log_control = wx.TextCtrl(self, wx.NewId(), style=wx.TE_MULTILINE|wx.TE_READONLY)
log_control.LoadFile('example.log')
EDIT:
This now works with the _on_close event! Thanks Fenikso