wxWidgets / Phoenix

wxPython's Project Phoenix. A new implementation of wxPython, better, stronger, faster than he was before.
http://wxpython.org/
2.22k stars 509 forks source link

wx.Bitmap locale error when loading PNG #769

Open Finemechanic opened 6 years ago

Finemechanic commented 6 years ago

Operating system: Windows 8.1 wxPython version: 3.6.2 Stock or custom build: stock Python version: Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)] on win32 Stock or custom build: stock

Description of the problem: I get assertion error when loading image into wx.Bitmap. It occurs only when .PNG is used. BMP, for example, loads fine. I'm using non-English version of Windows.

iconPlus = wx.Bitmap(u"plus.png") wx._core.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!

import wx

class HelloFrame(wx.Frame):

    def __init__(self, *args, **kw):
        # ensure the parent's __init__ is called        
        super(HelloFrame, self).__init__(*args, **kw)

        iconPlus = wx.Bitmap(u"plus.png")

if __name__ == '__main__':
    # When this module is run (not imported) then create the app, the
    # frame, show it, and start the event loop.

    app = wx.App()
    frm = HelloFrame(None, title='Hello World')
    frm.Show()
    app.MainLoop()
Metallicow commented 6 years ago

@Finemechanic Attach the png so it can be inspected also please. Does this happen with the specific file or others also?

RandyERaymond commented 6 years ago

Have tried including the specific file type:

init (self, name, type=BITMAP_TYPE_ANY) Loads a bitmap from a file or resource.

Parameters: name (string) – This can refer to a resource name or a filename under MS Windows and X. Its meaning is determined by the type parameter. type (BitmapType) – May be one of the wx.BitmapType values and indicates which type of bitmap should be loaded. See the note in the class detailed description. Note that the BITMAP_DEFAULT_TYPE constant has different value under different wxWidgets ports. See the bitmap.h header for the value it takes for a specific port.

Description Value
wx.BITMAP_TYPE_INVALID  
wx.BITMAP_TYPE_BMP  
wx.BITMAP_TYPE_ICO  
wx.BITMAP_TYPE_CUR  
wx.BITMAP_TYPE_XBM  
wx.BITMAP_TYPE_XBM_DATA  
wx.BITMAP_TYPE_XPM  
wx.BITMAP_TYPE_XPM_DATA  
wx.BITMAP_TYPE_TIFF  
wx.BITMAP_TYPE_TIF  
wx.BITMAP_TYPE_GIF  
wx.BITMAP_TYPE_PNG  
wx.BITMAP_TYPE_JPEG  
wx.BITMAP_TYPE_PNM  
wx.BITMAP_TYPE_PCX  
wx.BITMAP_TYPE_PICT  
wx.BITMAP_TYPE_ICON  
wx.BITMAP_TYPE_ANI  
wx.BITMAP_TYPE_IFF  
wx.BITMAP_TYPE_TGA  
wx.BITMAP_TYPE_MACCURSOR  
wx.BITMAP_TYPE_ANY  
Finemechanic commented 6 years ago

@Metallicow You may find the file attached. I've tried some other PNG files and they produce the same error. plus

@RandyERaymond I've tried to specify type = wx.BITMAP_TYPE_PNG and it doesn't help.

Metallicow commented 6 years ago

@Finemechanic This is your problem. iCCP: profile 'ICC Profile': 1000000h: invalid rendering intent

Not sure how you got that but you probably have a png that needs cleaned. At one point libpng started saying certain profiles were invalid and started giving me errors. If you use photoshop for example you will want to save your images without color management or profiles. You can clean/optimize your pngs with FileOptimizer https://nikkhokkho.sourceforge.io/static.php?page=FileOptimizer

I fixed your problem and attached optimized png.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import wx

class HelloFrame(wx.Frame):

    def __init__(self, *args, **kw):
        # ensure the parent's __init__ is called
        super(HelloFrame, self).__init__(*args, **kw)

        iconPlus = wx.Bitmap(u"plus.png")
        bmpbtn = wx.BitmapButton(self, id=-1, bitmap=iconPlus)

if __name__ == '__main__':
    # When this module is run (not imported) then create the app, the
    # frame, show it, and start the event loop.

    app = wx.App()
    frm = HelloFrame(None, title='Hello World')
    frm.Show()
    app.MainLoop()

plus

Finemechanic commented 6 years ago

@Metallicow Thank you for the solution - it works for me.

Still, I think there is a problem with wxPython/wxWidgets:

  1. Huge percentage of PNG files on internet causes the error. Even newly generated PNG file from Microsoft Paint does. I don't know whether the files are really corrupted or the library handles them improperly, but no other piece of software complains about the images.

  2. When the wxPython/wxWidget have to handle such a PNG file, it does something wrong with locales and crashes.

Update: Ok, I've found that libpng from some point started to complain about not-so-standard PNGs. What shall I do if I want a user to specify a file (I can't just send him away until he converts his files) and the issue with locales remains.

RobinD42 commented 6 years ago

I wouldn't be surprised if it was libpng that is changing the locale, perhaps in order to display an error message.

Metallicow commented 6 years ago

@Finemechanic I recall you can suppress/turn warnings off. I did it at one point, before deciding to optimize all my program pngs. I don't recall exactly how I did it at the moment. Look into that, its in the docs somewhere. It should probably work with this specific error/warning too.

Finemechanic commented 6 years ago

Ok. What about the issue with locale? Is it a bug? In libpng or wxPython?

Metallicow commented 6 years ago

Not sure about locale. You will have to investigate that yourself probably. The profile thing started when libpng was updated. You might be able to download a version before it was updated and test. That might help pinpoint your issue, but am not sure.

JanusHL commented 5 years ago

Hi, I'm spanish developer and I had the same problem, but googling it, I just founded a solution in stackoverflow: https://stackoverflow.com/questions/33505916/wx-image-is-throwing-pyassertionerror It seems that not all the .png files raises the error and could be some info inside them have to be with locale data at create time. Could be wxPython handles png data incorrectly...

Utumno commented 2 years ago

This is a serious problem for us, preventing us to move to python 3 (and wxpython phoenix). Whatever the deeper issue is (and it is a deep issue, see https://bugs.python.org/issue43552 and https://bugs.python.org/issue38805#msg373896 for what python does on startup) if the crash occurs on reading metadata could this be patched to not fail with an Assertion? There is no need to crash the wx App because we can't read a png. I don't know if it would even be possible to load the png at hand and ignore the metadata, but failing with a specialized exception like "MetadataError", that caller can catch and handle (displaying some default image for instance), is a much better solution than crashing.

Metallicow commented 2 years ago

There is a string mismatch in between python and wx. Not sure how the rest of the world will resolve this issue, but the two blatant options are monkey patch or rewrite code.

On Wed, Dec 29, 2021, 6:21 AM Utumno @.***> wrote:

This is a serious problem for us, preventing us to move to python 3 (and wxpython phoenix). Whatever the deeper issue is (and it is a deep issue, see https://bugs.python.org/issue43552 and https://bugs.python.org/issue38805#msg373896 for what python does on startup) if the crash occurs on reading metadata could this be patched to not fail with an Assertion? There is no need to crash the wx App because we can't read a png. I don't know if it would even be possible to load the png at hand and ignore the metadata, but failing with a specialized exception like "MetadataError", that caller can catch and handle (displaying some default image for instance), is a much better solution than crashing.

— Reply to this email directly, view it on GitHub https://github.com/wxWidgets/Phoenix/issues/769#issuecomment-1002546019, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABDTXRBRJW4KXLXGSXBWHE3UTLVKPANCNFSM4ESLLI7Q . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you were mentioned.Message ID: @.***>

swt2c commented 2 years ago

Does this only happen on Windows? @Utumno is your reproducer still the same as the OP's?

Utumno commented 2 years ago

Only happens on windows yes (I get user reports, but I can't check right now (not on a windows machine) - see https://github.com/wrye-bash/wrye-bash/issues/610 for the gory details).

swt2c commented 2 years ago

For those who experience this problem, do you still see it with the latest snapshot builds? https://wxpython.org/Phoenix/snapshot-builds/

Utumno commented 2 years ago

Back on windows - here is a reproducer that however sometimes terminates successfully:

import locale, wx, sys, os

print(f'Python {sys.version} on {sys.platform}')
print(f'Wx {wx.version()}')
print(f'getlocale: {locale.getlocale()}')
print(f'getdefaultlocale: {locale.getdefaultlocale()}')

app = wx.App()

for loc in ('en_GB', 'en_US', 'pl_PL',  # 'de_DE', 'sv_SE', 'fr_CM'
            ):
    print(f'setting wx locale to {loc}')
    cli_target = wx.Locale.FindLanguageInfo(loc)
    target_language = cli_target.Language
    target_locale = wx.Locale(target_language)
    target_name = target_locale.GetCanonicalName()
    wx.Bitmap(os.path.join(os.path.dirname(__file__), 'reload16.png'), wx.BITMAP_TYPE_PNG)
    # assert time.strptime('2006-01-01', '%Y-%m-%d')
    #   File "C:\_\Python39\lib\locale.py", line 501, in _parse_localename
    #     raise ValueError('unknown locale: %s' % localename)
    # ValueError: unknown locale: en-GB

Running this via pycharm repeatedly soon prints:

C:\Users\MrD\.virtualenvs\venvs\wrye-bash-wx-snapshot\Scripts\python.exe C:/Users/MrD/AppData/Roaming/JetBrains/PyCharm2021.3/scratches/scratch_1.py
Python 3.9.2 (tags/v3.9.2:1a79785, Feb 19 2021, 13:44:55) [MSC v.1928 64 bit (AMD64)] on win32
Wx 4.1.2a1.dev5310+af8cca51 msw (phoenix) wxWidgets 3.1.5
getlocale: ('English_United States', '1252')
getdefaultlocale: ('en_US', 'cp1252')
setting wx locale to en_GB
setting wx locale to en_US
setting wx locale to pl_PL
Traceback (most recent call last):
  File "C:\Users\MrD\AppData\Roaming\JetBrains\PyCharm2021.3\scratches\scratch_1.py", line 24, in <module>
    wx.Bitmap(os.path.join(os.path.dirname(__file__), 'reload16.png'), wx.BITMAP_TYPE_PNG)
wx._core.wxAssertionError: C++ assertion "strcmp(setlocale(0, 0), "C") == 0" failed at ..\..\src\common\intl.cpp(1704) 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!

Process finished with exit code 1

The bitmap is: reload16

This reproduces the random failures users reported - there is a race feeling in it. For different users it would fail on different bitmaps and various combinations of setting the C locale, overriding the wx.App and so on would fail for different locales while work for others. Even running the tests locally sometimes fail and sometimes doesn't

HTH!

qu1ck commented 1 year ago

Just in case this helps anyone who ended up here by googling the error and coming from https://github.com/wxWidgets/Phoenix/issues/1751

I found 2 different solutions that worked for me:

  1. app.SetAssertMode(wx.APP_ASSERT_SUPPRESS). Somehow prevents libpng from crashing the app on "invalid" files.
  2. Create the app as the very first step of the main, before I even import anything else beside wx.

Either of those fixes worked. 2nd fix also removed debug assert popups about failure to initialize wxPlatformInfo which were impossible to get rid of even with wx.DisableAsserts() because they happened during import of wx itself.

Locales in python are a mess...