I have an IpAddrCtrl element from wx.lib.masked in my simple test:
import wx
import wx.lib.masked as masked
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,500))
self.ipaddr1 = masked.IpAddrCtrl(self, -1, style=wx.TE_PROCESS_TAB)
self.grid_ip = wx.FlexGridSizer(cols=2, vgap=10, hgap=10)
self.grid_ip.Add(self.ipaddr1, 0, wx.ALIGN_LEFT)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.grid_ip, 0, wx.ALIGN_LEFT | wx.ALL, border=5)
self.SetSizer(self.sizer)
self.Bind(wx.EVT_TEXT, self.OnIpAddrChange, id=self.ipaddr1.GetId())
self.Show(True)
def OnIpAddrChange(self, event):
print(self.ipaddr1.GetAddress())
print(self.ipaddr1.IsValid())
if __name__ == '__main__':
app = wx.App(False)
frame = MainWindow(None, "IpAddrCtrl sample")
app.MainLoop()
After application starts, I try to insert correct numeric values in my IpAddrCtrl element, but it falls with following log:
<BaseMaskedTextCtrl: . . . >
False
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 3016, in _OnChar
keep_processing = self._keyhandlers[key](event)
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 3937, in _OnChangeField
self._AdjustField( pos )
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 4291, in _AdjustField
newfield = field._AdjustField(slice)
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/lib/masked/maskededit.py", line 1649, in _AdjustField
intStr = str(long(intStr))
ValueError: invalid literal for long() with base 10: '.'
Here a screencast how does it actually works.
Similar demo from wxPython project causes same problems for me.
My environment: Debian Stretch with Python 2.7.13, wxPython 3.0.2 and GTK2 2.24.31-2 from official stable repositories.
Any advices?
Thanks for the help!
Update:
I try to use same sample on Win32 platform and there are no any problems with IpAddrCtrl. After some debugging I found out that the difference in GTK/Win versions in additional EVT_TEXT event call that occurs in IpAddrCtrl when I try to edit selected text region. That causes unnecessary _OnTextChange event handler call, that brokes input.
That strange behavior described on wxPython wiki and commented in a sources of base class for IpAddrCtrl, but currently a have no idea how to fix it.
It's a horrible control, which as far as I can tell, simply doesn't function intuitively at all.
You have to press back space,to clear the field, as you enter each segment of the ip address or it gets itself in a complete tizzy.
Replace your lines
ipaddr = self.FindWindowById(event.GetId())
print ipaddr
print ipaddr.IsValid()
with
print (self.ipaddr1.GetAddress())
print (self.ipaddr1.IsValid())
Clearly, it's a function that needs work, as it is broken in wxpython 4 as well.
Related
I am using wxPython on Python 2.7.
I would like some help with creating a button with bitmap images.
I am using this video https://www.youtube.com/watch?v=Y7f0a7xbWHI,
and I followed the codes and typed
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id,'Frame aka window',size=(300,200))
panel=wx.Panel(self)
pic=wx.Image("back.bmp", wx.BITMAP_TYPE_BMP).ConvertToBitmap()
self.button=wx.BitmapButton(panel, -1, pic, pos=(10,10))
self.Bind(wx.EVT_BUTTON, self.doMe, self.button)
self.button.SetDefault()
def doMe(self, event):
self.Destroy
to create a button with an image. I got an error stating Invalid Image.
I saved the bitmap image in a folder that has .py file I am working with.
I feel like I am saving the image in the wrong place?
Thank you in advance.
The error I received
Traceback (most recent call last):
File "C:\Python27\FrameWindow.py", line 81, in <module>
frame=bucky(parent=None,id=-1)
File "C:\Python27\FrameWindow.py", line 17, in __init__
pic=wx.Image("back.bmp", wx.BITMAP_TYPE_BMP).ConvertToBitmap()
File "C:\Python27\lib\site-packages\wx\core.py", line 708, in _Image_ConvertToBitmap
bmp = wx.Bitmap(self, depth)
wxAssertionError: C++ assertion "image.IsOk()" failed at ..\..\src \msw\bitmap.cpp(922) in wxBitmap::CreateFromImage(): invalid image
I got it to work by adding
"locale = wx.Locale(wx.LANGUAGE_ENGLISH)"
under
class MainFrame(wx.Frame):
def __init__(self):
now I do not get the error message, and it runs as it should.
The error code I received for this problem was:
Traceback (most recent call last):
File "C:\Python27\panel test.py", line 21, in <module>
frame = MainFrame()
File "C:\Python27\panel test.py", line 11, in __init__
pic = wx.Bitmap("back.bmp", wx.BITMAP_TYPE_ANY)
wxAssertionError: C++ assertion "strcmp(setlocale(LC_ALL, NULL), "C") == 0" failed at ..\..\src\common\intl.cpp(1579) in wxLocale::GetInfo(): You probably called setlocale() directly instead of using wxLocale and now there is a mismatch between C/C++ and Windows locale.
Things are going to break, please only change locale by creating wxLocale objects to avoid this!
The error given by my primary question was solved by the codes given by Rolf by Saxony.
Use wx.BITMAP_TYPE_ANY rather than wx.BITMAP_TYPE_BMP it takes the guessing out of whether it really is a BMP or not
or use wx.Bitmap() directly.
import wx
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,-1,'Frame aka window',size=(300,200))
panel=wx.Panel(self)
#
# Use wx.BITMAP_TYPE_ANY rather than wx.BITMAP_TYPE_BMP
#
#pic=wx.Image("Discord.bmp", wx.BITMAP_TYPE_ANY).ConvertToBitmap()
#
# or use wx.Bitmap()
pic = wx.Bitmap("Discord.bmp", wx.BITMAP_TYPE_ANY)
#
self.button=wx.BitmapButton(panel, -1, pic, pos=(10,10))
self.Bind(wx.EVT_BUTTON, self.doMe, self.button)
self.Show()
def doMe(self, event):
self.Destroy()
if __name__ == "__main__":
app = wx.App()
frame = MainFrame()
app.MainLoop()
In principle you should name your image/s with a full pathname. If you have many images, then have an image directory and join that directory name to your image names as you use them, which again gives you a full path (/home/images/back_button.bmp)
I'm new to Python (and programming in general) so forgive me if this is a dumb question.
I'm following a tutorial in a book for creating a GUI in Python. Right now I'm learning about how to make a Spin Control increment some static text. When I run it, the spin control shows up but the console says "value = event.GetPosition()
AttributeError: 'CommandEvent' object has no attribute 'GetPosition'"
The code is:
import wx
class Frame(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None,\
title=title, size=(300,250))
panel = wx.Panel(self)
sc = wx.SpinCtrl(panel, value='0', pos=(130, 50), size=(70, 25))
self.valueText = wx.StaticText(panel, label='', pos=(130,80))
sc.Bind(wx.EVT_SPINCTRL, self.spinControl)
def spinControl(self, event):
# Get spin control value
value = event.GetPosition()
# Update static text
self.valueText.SetLabel(str(value))
app = wx.App()
frame = Frame("wxPython Widgets!")
frame.Show()
app.MainLoop()
That code is literally copy/pasted from the book's website. I have wxPython installed and everything works perfectly up to that point.
Halp!
I've been working through that same book Python in a Day 2. I believe the content is already outdated.
If you change the line with GetPosition() to
value = event.GetEventObject().GetValue()
is correct. I can confirm that it works for Python 2.7.11.
Jeremiah
If you change the line with GetPosition() to
value = event.GetEventObject().GetValue()
it will work. The event just doesnt have the GetPosition attribute, so it cannot eb executed.
If you want to check which functions and attributes are available, you can use
print(dir(event))
This will show you everything which is available inside the event.
Michael
I am learning to use wxPython to build a dialogue based program.
I tried following code (simply copied from wxPython Demo):
import wx
#---------------------------------------------------------------------------
class TestPanel(wx.Panel):
def __init__(self, parent, log):
self.log = log
wx.Panel.__init__(self, parent, -1)
b = wx.Button(self, -1, "Create and Show a DirDialog", (50,50))
self.Bind(wx.EVT_BUTTON, self.OnButton, b)
def OnButton(self, evt):
# In this case we include a "New directory" button.
dlg = wx.DirDialog(self, "Choose a directory:",
style=wx.DD_DEFAULT_STYLE
#| wx.DD_DIR_MUST_EXIST
#| wx.DD_CHANGE_DIR
)
# If the user selects OK, then we process the dialog's data.
# This is done by getting the path data from the dialog - BEFORE
# we destroy it.
if dlg.ShowModal() == wx.ID_OK:
self.log.WriteText('You selected: %s\n' % dlg.GetPath())
# Only destroy a dialog after you're done with it.
dlg.Destroy()
#---------------------------------------------------------------------------
def runTest(frame, nb, log):
win = TestPanel(nb, log)
return win
#---------------------------------------------------------------------------
overview = """\
This class represents the directory chooser dialog. It is used when all you
need from the user is the name of a directory. Data is retrieved via utility
methods; see the <code>DirDialog</code> documentation for specifics.
"""
if __name__ == '__main__':
import sys,os
import run
run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
I ran above code both in Python IDLE and Apatana Studio 3. Here is what I got.
In Python IDLE, I've got:
IDLE Subprocess: no IP port passed in sys.argv.
And in Apatana Studio 3, I've got:
Traceback (most recent call last):
File "C:\Users\User\My Documents\Aptana Studio 3 Workspace\Test Dialogue\main.py", line 61, in
import run ImportError: No module named run
May I know what am I wrong? Thanks a lot. :)
The ImportError is the Python interpreter (the program that runs your Python code) letting you know it cannot find the module (.py file) you are trying to import. Specifically, the error is saying it cannot find the module "run" which you have asked it to import on line 61.
When you do an import in Python, the interpreter searches a bunch of places for the module. One of those is the current directory, and the rest are standard places like where Python libraries are installed. This page has some info about it: http://docs.python.org/2/tutorial/modules.html#the-module-search-path. You'll actually get the same ImportError if you run the program from the command line. It's a Python error, not an Apatana Studio 3 error.
So if you copy "run.py" into the directory with your Python file, the Python interpreter will be able to easily find it when you asks it to import. Another way is to leave the run.py module where it is and change the sys.path at runtime, or add the module location to the PYTHONPATH variable (see link above for more info).
The run.py module isn't needed for what you are trying to achieve though. Here's an example of your code without the run.py module being imported. I'll warn that I'm new to wxPython myself so there may be better ways to do it ;-)
import wx
# This Log class is copied from the run module
class Log(object):
def WriteText(self, text):
if text[-1:] == '\n':
text = text[:-1]
wx.LogMessage(text)
write = WriteText
class TestPanel(wx.Panel):
def __init__(self, parent, log):
self.log = Log()
wx.Panel.__init__(self, parent, -1)
b = wx.Button(self, -1, "Create and Show a DirDialog", (50,50))
self.Bind(wx.EVT_BUTTON, self.OnButton, b)
def OnButton(self, evt):
# In this case we include a "New directory" button.
dlg = wx.DirDialog(self, "Choose a directory:",
style=wx.DD_DEFAULT_STYLE
#| wx.DD_DIR_MUST_EXIST
#| wx.DD_CHANGE_DIR
)
# If the user selects OK, then we process the dialog's data.
# This is done by getting the path data from the dialog - BEFORE
# we destroy it.
if dlg.ShowModal() == wx.ID_OK:
self.log.WriteText('You selected: %s\n' % dlg.GetPath())
# Only destroy a dialog after you're done with it.
dlg.Destroy()
class Frame ( wx.Frame ):
def __init__( self, parent ):
wx.Frame.__init__(self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size(300, 150))
panel = TestPanel(self, -1)
class App(wx.App):
def OnInit(self):
self.frame = Frame(parent=None)
self.frame.Show()
self.SetTopWindow(self.frame)
return True
if __name__ == '__main__':
app = App()
app.MainLoop()
I'm not sure what's happening with the error in IDLE though. That's weird!
As the title says when I implement code to clear hinted text and it is run the application crashes.
As far as I know this is only on Mac OS X 10.8 but that is also all I have been able to run it on.
On other code it does run and only once I enter text into it (After giving focus) does it crash. But this app crashes immediately (I think something to do with no other widgets and so it get focus right away). Updated example so it no longer crashes right away now you have to click on the combo box and try to type in it for it to crash.
This however does not occur if the text is anything but "" it seems.
Code
import wx
class MyCrashyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
# another widget to take focus at first otherwise it crashes instantly!
sizer = wx.BoxSizer(wx.VERTICAL)
self.text_ctrl = wx.TextCtrl(self, -1, value = "There are major problems here.\nWithout this to auto take focus this will crash immediatly just by trying to clear the hint.\nNow you have to click on the combo ctrl and try to type.", style = wx.TE_MULTILINE)
self.search_ctrl = wx.ComboBox(self, -1)
self.search_ctrl.SetMinSize((650, -1))
self.search_ctrl.SetSize((650, -1))
self.search_ctrl.SetHint("This is are hint text; once it is clear and you try to type something in it it will crash on Mac OS X")
sizer.Add(self.text_ctrl, flag = wx.EXPAND)
sizer.Add(self.search_ctrl)
self.SetSizer(sizer)
self.FirstTimeSearchGetsFocus = True
self.Bind(wx.EVT_BUTTON, lambda e: e.Skip(), self.text_ctrl)
self.search_ctrl.Bind(wx.EVT_SET_FOCUS, self.OnSearchFocus)
self.text_ctrl.SetFocus()
def OnSearchFocus(self, event):
print "Search Focus"
if 1==1:
print "First time"
# clear the hinted text
self.search_ctrl.SetHint("")
self.search_ctrl.Clear()
self.search_ctrl.Refresh()
self.FirstTimeSearchGetsFocus = False
event.Skip()
if __name__ == "__main__":
app = wx.App(False)
f = wx.Frame(None, -1)
MyCrashyPanel(f)
f.Show()
app.MainLoop()
Crash Report
[Too big get here http://pastebin.com/9B1Sgh3P ]
If it crashes, it's a bug in wxWidgets, so the only things to do are to:
Try with a later version, i.e. 2.9.5 or svn/git if you can build it yourself.
Report the bug if it still persists there, following the usual guidelines.
This simple line:
wx.MessageBox('Foo', 'Bar', wx.OK | wx.ICON_ERROR)
Gives me a message box with an error icon and the Windows error noise (this is not the same noise as wx.Bell()). I would like to create a custom error dialog for uncaught exceptions, where the traceback is available in a text control and such, and I would like to include both the Windows error icon and the noise. I know that both differ between versions of Windows, and the error noise can even be customized.
Is there any straight-forward way of using these native Windows resources with wxPython? Bonus question; if the answer is no, what would be the most straight-forward way of doing what I'm trying to do?
Results after accepted answer:
I just wanted to show off the results after Anonymous Coward's excellent answer, as they far exceeded my expectations. This is the error dialog that now pops up on unhandled exceptions (on Windows 8):
It also packs the modern Windows "UNNK!" error sound. This is the code behind the dialog. I put it in a separate module that overrides sys.excepthook when it's imported:
"""This module, when imported, overrides the default unhandled exception hook
with one that displays a fancy wxPython error dialog."""
import sys
import textwrap
import traceback
import winsound
import wx
def custom_excepthook(exception_type, value, tb):
dialog = ExceptionDialog(exception_type, value, tb)
dialog.ShowModal()
# Override sys.excepthook
sys.excepthook = custom_excepthook
class ExceptionDialog(wx.Dialog):
"""This class displays an error dialog with details information about the
input exception, including a traceback."""
def __init__(self, exception_type, exception, tb):
wx.Dialog.__init__(self, None, -1, title="Unhandled error",
style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
self.SetSize((640, 480))
self.SetMinSize((420, 200))
self.exception = (exception_type, exception, tb)
self.initialize_ui()
winsound.MessageBeep(winsound.MB_ICONHAND)
def initialize_ui(self):
extype, exception, tb = self.exception
panel = wx.Panel(self, -1)
# Create the top row, containing the error icon and text message.
top_row_sizer = wx.BoxSizer(wx.HORIZONTAL)
error_bitmap = wx.ArtProvider.GetBitmap(
wx.ART_ERROR, wx.ART_MESSAGE_BOX
)
error_bitmap_ctrl = wx.StaticBitmap(panel, -1)
error_bitmap_ctrl.SetBitmap(error_bitmap)
message_text = textwrap.dedent("""\
I'm afraid there has been an unhandled error. Please send the
contents of the text control below to the application's developer.\
""")
message_label = wx.StaticText(panel, -1, message_text)
top_row_sizer.Add(error_bitmap_ctrl, flag=wx.ALL, border=10)
top_row_sizer.Add(message_label, flag=wx.ALIGN_CENTER_VERTICAL)
# Create the text control with the error information.
exception_info_text = textwrap.dedent("""\
Exception type: {}
Exception: {}
Traceback:
{}\
""")
exception_info_text = exception_info_text.format(
extype, exception, ''.join(traceback.format_tb(tb))
)
text_ctrl = wx.TextCtrl(panel, -1,
style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
text_ctrl.SetValue(exception_info_text)
# Create the OK button in the bottom row.
ok_button = wx.Button(panel, -1, 'OK')
self.Bind(wx.EVT_BUTTON, self.on_ok, source=ok_button)
ok_button.SetFocus()
ok_button.SetDefault()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(top_row_sizer)
# sizer.Add(message_label, flag=wx.ALL | wx.EXPAND, border=10)
sizer.Add(text_ctrl, proportion=1, flag=wx.EXPAND)
sizer.Add(ok_button, flag=wx.ALIGN_CENTER | wx.ALL, border=5)
panel.SetSizer(sizer)
def on_ok(self, event):
self.Destroy()
The only improvement I could wish for is for the static text to flow and wrap automatically according to the width of the dialog, but I couldn't be bothered to make a custom control class just for that.
The wx.MessageBox calls the os-specific standard message/alert dialog box (on windows, that's MessageBox() from user32.dll). wxWidgets simply translates the flags you provide (like wx.OK and wx.ICON_INFORMATION) to the os-specific flags and options for the native message box.
You can obtain the os-specific icons through the wx.ArtProvider.
As far as sounds go, wx.Sound can only play sound files. However, if the application does not have to be portable, you can use the winsound module.
import wx
import winsound # windows only
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,wx.ID_ANY)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add( wx.StaticBitmap(self,bitmap=wx.ArtProvider.GetBitmap(wx.ART_INFORMATION)) )
sizer.Add( wx.StaticBitmap(self,bitmap=wx.ArtProvider.GetBitmap(wx.ART_QUESTION)) )
sizer.Add( wx.StaticBitmap(self,bitmap=wx.ArtProvider.GetBitmap(wx.ART_WARNING)) )
sizer.Add( wx.StaticBitmap(self,bitmap=wx.ArtProvider.GetBitmap(wx.ART_ERROR)) )
self.SetSizerAndFit(sizer)
self.Show()
winsound.MessageBeep(winsound.MB_ICONASTERISK)
winsound.PlaySound('SystemHand', winsound.SND_ASYNC | winsound.SND_ALIAS)
app = wx.PySimpleApp()
Frame()
app.MainLoop()