Is it possible to create a message box (with wx.MessageDialog or anything else) without parent window ?
For example, I sometimes may want to display an error message before the GUI has really started. Then I would need to be able to display a message box before having a parent window :
With parent = None, this doesn't work :
wx.MessageDialog(parent, 'This is a message box.', 'Test', wx.OK | wx.ICON_INFORMATION).ShowModal()
How to display a message box without a parent window ?
Just saw this old question and wanted to answer it, better late than never:
By default, the main application window is used as the dialog parent even if no parent is specified explicitly, because this is what you want in 99% of cases -- modal dialogs without parent/owner window are quite unusual. If you really, really need to prevent the dialog from having a parent, you must use wx.DIALOG_NO_PARENT style explicitly.
It should work, try this:
import wx
app = wx.App()
wx.MessageDialog(None, 'This is a message box.', 'Test', wx.OK | wx.ICON_INFORMATION).ShowModal()
frame = wx.Frame(None)
frame.Center()
frame.Show()
app.MainLoop()
I know this is an old question, but I believe that parent=None does not work as one might expect. Consider the example above, but with the wx.Frame shown first, and the wx.MessageDialog after, like so:
import wx
app = wx.App()
frame = wx.Frame(None)
frame.Center()
frame.Show()
wx.MessageDialog(None, 'This is a message box.', 'Test', wx.OK | wx.ICON_INFORMATION).ShowModal()
app.MainLoop()
The result is a wx.Frame with the wx.MessageDialog shown on top of it (as expected), but the wx.Frame cannot be resized or draged around the screen (not expected). The wx.MessageDialog can be draged around the screen, but the wx.MessageDialog moves with it (not expected). The two frames clearly do not work independent of each other, and it seems the wx.MessageDialog is owned by the wx.Frame. I therefore think that wxPython applies some magic that is not obvious; at least I couldn't see anything in the docs.
Without parent frame.
There seems no need to use a frame for showing a 'stand alone' dialog. This works fine. (Tested only on Win10.)
Apparently wxpython takes the dialog, which is of course also just a window, as the 'frame' to display.
import wx
# -------------------------------------------------------
def wx_ask_question_windowed(question, caption):
app = wx.App()
dlg = wx.MessageDialog(None, question, caption, wx.YES_NO | wx.ICON_INFORMATION)
dlg.Center()
dlg_result = dlg.ShowModal()
result = dlg_result == wx.ID_YES
dlg.Destroy()
app.MainLoop()
app.Destroy()
return result
# ==============================================================
def main():
if wx_ask_question_windowed('Do you like this?', 'A windowed question'):
print('You like it')
else:
print("You don't like it")
# ==============================================================
if __name__ == '__main__':
main()
Related
I am trying to get a searchctrl in wxPython. However I am not getting exactly what I want.
I get this:
But I would like to get the SearchCtrl like:
I know that there isn't a big difference, it is just for visual reasons.
I declare my SearchCtrl as:
self.searchControl = wx.SearchCtrl(panel, -1, style=wx.TE_PROCESS_ENTER)
Does anybody know how can I declare it in order to get the SearchCtrl as I want?
There are three differences that I can see between what you have and what you want:
The cancel button (X in a circle)
The menu indicator (the arrow next to the magnifying glass).
The location of the word "Search"
None of these three differences are affected by the declaration.
To get the cancel button to show up, call:
self.searchControl.ShowCancelButton(True)
To get the menu indicator to show up, call:
self.SetMenu(menu)
To get the text to appear in the right place, prevent the sizer from vertically resizing your control.
For example:
#!/usr/bin/env python
import wx
app = wx.App(False)
frame = wx.Frame(None, wx.ID_ANY, "Hello World")
sizer = wx.BoxSizer(wx.HORIZONTAL)
menu = wx.Menu()
menu.Append(wx.ID_ABOUT, 'About')
search = wx.SearchCtrl(frame)
search.ShowCancelButton(True)
search.SetMenu(menu)
sizer.Add(search, 0)
frame.SetSizer(sizer)
frame.SetAutoLayout(1)
sizer.Fit(frame)
frame.Show()
app.MainLoop()
yields this:
Im making a python program and in some functions it needs to hide the X and expand window buttons, how would i do it? Im using WxPython, how would I put this in?
The widgets in the window frame are defined as part of the window's style: CLOSE_BOX, MINIMIZE_BOX, and MAXIMIZE_BOX.
So, when you create the window, just leave those styles out.
If you're using a wx.Frame subclass, note that DEFAULT_FRAME_STYLE includes these values, so you will have to mask them out:
style = wx.DEFAULT_FRAME_STYLE & (~wx.CLOSE_BOX) & (~wx.MAXIMIZE_BOX)
super().__init__(whatever, args, you, use, style=style)
If you want to change them after creation, you use SetWindowStyle:
style = self.GetWindowStyle()
self.SetWindowStyle(style & (~wx.CLOSE_BOX) & (~wx.MAXIMIZE_BOX))
self.Refresh()
However, notice that the documentation of that function says:
Please note that some styles cannot be changed after the window creation and that Refresh() might need to be called after changing the others for the change to take place immediately.
And, from what I can tell, on Windows, if you create a window with a close box and then remove it later in this way, it doesn't actually go away. It does disable, which may be good enough. But if not, there's probably no way to do what you want without either reaching underneath wx to the native Windows API (which gets very tricky), or drawing the widgets on the frame manually (which gets even more tricky, especially if you care about looking right on different versions of Windows—not to mention porting to other platforms).
I wrote about Frame styles a while ago on my blog. To remove all the buttons, you could do this:
import wx
########################################################################
class NoSystemMenuFrame(wx.Frame):
"""
There is no system menu, which means the title bar is there, but
no buttons and no menu when clicking the top left hand corner
of the frame
"""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
no_sys_menu = wx.CAPTION
wx.Frame.__init__(self, None, title="No System Menu", style=no_sys_menu)
panel = wx.Panel(self)
self.Show()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
frame = NoSystemMenuFrame()
app.MainLoop()
I tried setting the style to wx.DEFAULT_FRAME_STYLE & (~wx.CLOSE_BOX) & (~wx.MAXIMIZE_BOX) and to wx.DEFAULT_FRAME_STYLE^(wx.CLOSE_BOX|wx.MAXIMIZE_BOX), but both of those seem to only remove the Close box. For some reason, the Maximize button is still there on my Xubuntu machine.
I have a notebook whose parent is the main frame of the application. The main frame also has a panel showing a chart to the side of the notebook, a menu bar and a status bar.
The notebook has a couple of pages and each page has some nested panels.
I'd like the callbacks for buttons in those panels to be able to talk to the main frame.
At the moment, that means a ridiculous chain of 'parents'. For example, to get to status bar from a panel on a notebook page I would do:
stat = self.parent.parent.parent.status_bar
The first parent is the notebook page, the second parent is the notebook and finally the last parent is the main frame.
This leads to very obtuse code...
Naturally you can see how this might get worse if I wanted to talk between elements on the panel adjacent to the notebook or nest the notebook in it's own panel..
Any tips?
There is a simple way to get your Main Frame.
Since you can get your app instance anywhere in your code with "wx.GetApp()", then you can set your Mainframe into your app instance, it would be easy to fecth.
Please try following simple sample:
import wx
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
btn = wx.Button(wx.Panel(self), label = "test")
self.Bind(wx.EVT_BUTTON, self.onButton)
def onButton(self, evt):
print "onButton"
app = wx.GetApp()
print app.Myframe
app = wx.App()
frame = TestFrame()
frame.Center()
frame.Show()
app.Myframe = frame
app.MainLoop()
If you need to get access to the top frame, you should be able to use wx.GetTopLevelParent(). Personally, I think pubsub is probably the easiest way to call various classes in wxPython and it's pretty clean too. Plus if you need to call multiple frames or panels or whatever, you can have them all "subscribe" to the same message name and then publish a message for all of them to pick up.
Here's a tutorial for pubsub: http://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/
Solved:
Thanks to Aya's answer below I now know that the issue was caused by self.panel = wx.Panel(self, -1) on line 18. I created a panel and didn't attach anything to it. The original issue description is still below for reference.
My Google-fu has failed me. I'm building the text editor that you can find here, written in Python with wxPython:
https://github.com/joshsaintjacque/py-ed/blob/master/pyed.py
The issue that I'm running into is this: when I open a text file (the only functionality built in at this point) that's larger than the viewable area in the TextCtrl the scroll bar remains disabled until the window is re-sized, then it works fine.
I know that the act of re-sizing the window is running some command that I'm neglecting to include in my OpenFile function (or perhaps in init), but I can't figure out what.
Any thoughts anyone has that could lead me in the right direction would be greatly appreciated.
Thanks!
+1 for including a link to the full source code - makes it so much easier to test.
I couldn't reproduce the fault you describe on wxPython 2.8.12 on Win32, but upon running your code, I found a seemingly extraneous wx.Panel object being created on pyed.py line 18...
self.panel = wx.Panel(self, -1)
...which seems to be interfering with the correct operation of the program. After commenting out that line, it seems to work fine.
A couple of other things I noticed: line 56...
self.SetTitle("PyEd - Editing ... " + filename)
...should probably be put in the preceding if-block, otherwise you'll get an error if the user clicks "Cancel" on the wx.FileDialog, and on line 16...
wx.Frame.__init__(self, parent, id, 'PyEd', (-1, -1), wx.Size(640, 480))
...if you use keyword args rather than positional args...
wx.Frame.__init__(self, parent=parent, id=id, title='PyEd', size=wx.Size(640, 480))
...you needn't bother re-specifying the default value for the window position, which is also slightly safer, in case the wxPython developers decide to change the defaults in a future version.
You can also factor out constant values, and the optional creation of the wx.Size object to reduce that line to...
wx.Frame.__init__(self, parent=None, title='PyEd', size=(640, 480))
Finally, with regards to IDs: in most cases you'll probably find they're of little use. Where they come in handy is where you want many similar controls, and it makes more sense to have them handled by a single event handler function.
Consider this example...
def create_buttons(parent):
parent.button1 = wx.Button(label='Button 1')
parent.button2 = wx.Button(label='Button 2')
parent.button3 = wx.Button(label='Button 3')
parent.button1.Bind(wx.EVT_BUTTON, on_button_1)
parent.button2.Bind(wx.EVT_BUTTON, on_button_2)
parent.button3.Bind(wx.EVT_BUTTON, on_button_3)
def on_button_1(event):
print 'You clicked button 1'
def on_button_2(event):
print 'You clicked button 2'
def on_button_3(event):
print 'You clicked button 3'
...which is fine, but if you need, say, 100 buttons, you may prefer to implement it like this...
def create_buttons(parent):
parent.buttons = [wx.Button(id=i, label='Button %d' % i) for i in range(100)]
parent.Bind(wx.EVT_BUTTON, on_button)
def on_button(event):
button_id = event.GetId()
print 'You clicked button %d' % button_id
Oh, and be careful using id as a variable name, because it's also a Python built-in function name.
It looks as if you're not setting the min or max size hints for the window, nor are you calling Self.Fit() to fit the box sizer to the window size (or is it the other way round? I'm rusty on my wxPython...)
Right where you call self.SetSizer(sizer), you should be able to fix this by adding:
self.Fit()
self.SetSizeHintSz(minSize=wx.Size(640, 480))
You may be able to get around the separate call to self.Fit() by using self.SetSizerAndFit()
(edited for spelling.)
My code is pretty straight forward, but I keep getting the error below. I researched the error and it pretty much says IDLE and my own GUI are screwing each other up, but I don't really know how to avoid it. I mean, if I just click on the .py file for my GUI without having IDLE open, I get the same error.
Any ideas?
Python 2.7
Windows XP
import wx
class applicationName(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, 'Title', size=(300,200))
panel = wx.Panel(self)
box = wx.TextEntryDialog(None, "How old are you?", "Title", "default text")
if box.ShowModal() == wx.ID_OK:
answer = box.GetValue()
if __name__ =='__main__':
app = wx.PySimpleApp()
frame = applicationName(parent=None, id=-1)
frame.Show()
app.MainLoop()
Error:
PyNoAppError: The wx.App object must be created first!
I guess you encountered this problem when you were debugging your program second time.
You can add the line at the end of the code.
del app
I hope it can help you.
Your __init__ function is not indented properly. It should be
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, 'Title', size=(300,200))
panel = wx.Panel(self)
box = wx.TextEntryDialog(None, "How old are you?", "Title", "default text")
if box.ShowModal() == wx.ID_OK:
answer = box.GetValue()
Quoted from: http://wxpython-users.1045709.n5.nabble.com/PyNoAppError-The-wx-App-object-must-be-created-first-td2362821.html
The key is to use an editor/IDE that runs the Python code you are
editing in an external process, rather than running it in the same
process as the editor itself.
Try closing the terminal/console and re-running it (if the option is available).
worked for me when i got this massage in Spyder (3.3.2) when ran similar code to the above.
Running this inside Psychopy builder, adding the following to the beginning of the experiment helped:
import wx
tmpApp = wx.PySimpleApp()
I had the same problem, but the:
del app
close the window and stop the kernel, so it was not of great help
I found that this worked for me:
app=[]; app = wx.App(None)
whitout the app=[]-part, the program run once, but not the second time when it stops and gives the "wx.app object must be created first"-error
Hope this can be of use for others.
Per