簡體   English   中英

為什么 wx.TextCtrl.SetStyle 對表情符號處理不當?

[英]Why does wx.TextCtrl.SetStyle mishandle emojis?

我正在 Python 3.7.3 中使用 wxPython 4.0.4 開發應用程序,但在嘗試為 wx.TextCtrl 中的 UTF-8 文本着色時遇到了問題。 基本上,盡管在 Python 中對某些字符進行了正確計數,但似乎某些字符在 wxPython 中的計數不正確。

我最初認為所有多字節字符都被錯誤計數,但是,我下面的示例代碼表明情況並非如此。 這似乎是 wx.TextCtrl.SetStyle function 中的一個問題。

import wx
import wx.richtext as rt
app = wx.App()

test_str1 = '''There are no multibyte characters '''
test_str2 = '''blah ble blah\n'''
test_str3 = '''“these are multibyte quotes” '''
test_str4 = '''more single byte chars!\n'''
test_str5 = '''this comma’s represented by multiple bytes\n'''
test_str6 = '''why do emojis 💨 💨 💨 seem to break TextCtrl.SetStyle 💨 💨 💨\n'''
test_str7 = '''more single byte characters\n'''
test_str8 = '''to demonstrate the issue.'''

def main():
    main = TestFrame()
    main.Show()
    app.MainLoop()

class TestFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="TestFrame")
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel = TestPanel(self)
        sizer.Add(self.panel, proportion=1, flag=wx.EXPAND)

class TestPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.text = wx.TextCtrl(self, wx.ID_ANY, style=(wx.TE_MULTILINE|wx.TE_RICH|wx.TE_READONLY))
        self.raw_text = ""
        self.styles = []
        self.AddColorText(test_str1, wx.BLUE)
        self.AddColorText(test_str2, wx.RED)
        self.AddColorText(test_str3, wx.BLUE)
        self.AddColorText(test_str4, wx.RED)
        self.AddColorText(test_str5, wx.BLUE)
        self.AddColorText(test_str6, wx.RED)
        self.AddColorText(test_str7, wx.BLUE)
        self.AddColorText(test_str8, wx.RED)
        self.text.SetValue(self.raw_text)
        for s in self.styles:
            self.text.SetStyle(s[0], s[1], s[2])
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.text, proportion=1, flag=wx.EXPAND)
        self.SetSizer(sizer)

    def AddColorText(self, text, wx_color):
        start = len(self.raw_text)
        self.raw_text += text
        end = len(self.raw_text)
        self.styles.append([start, end, wx.TextAttr(wx_color)])

if __name__ == "__main__":
    main()

在此處輸入圖像描述

MS Windows 在內部使用 UTF-16,在PEP 393之前,CPython Unicode 在 Windows 上也是 16 位的。 但是使用 PEP 393 CPython 現在可以更清晰地表示所有 Unicode 代碼點,因此一個 Unicode 代碼點的字符串長度始終為 1。

另一方面,MSWin 不能。 因此 wxPython 必須在將字符串發送到操作系統之前將其轉換為 UTF-16。 對於 Unicode 的基本多語言平面中的所有內容,這是您將遇到的大部分內容,這很好,因為一個 Unicode 代碼點變成了一個 UTF-16 字符(兩個字節)。

但是那些新的 Emoji 不在 BMP 中,所以它們在 UTF-16 中超過了兩個字節。 And wxPython fails to account for that: If wxPython passes the start and end counters straight through to an underlying Windows function, then they will be off after an Emoji, because the values your are given count Unicode code points, and the values Windows expects are UTF-16 字符計數。

您可以自己計算 UTF-16 偏移量以傳遞給 SetStyle:

utf16start = len(self.raw_text[:start].encode('utf-16'))
utf16end = utf16start + len(self.raw_text[start:end].encode('utf-16'))

可以說這是 wxPython 中的一個錯誤,您應該將它報告給wxPython 問題跟蹤器

看來我的問題是使用 python 標准函數而不是 wx 庫函數來計算長度。 下面的代碼解決了我的問題。

import wx
import wx.richtext as rt
app = wx.App()

test_str1 = '''There are no multibyte characters '''
test_str2 = '''blah ble blah\n'''
test_str3 = '''“these are multibyte quotes” '''
test_str4 = '''more single byte chars!\n'''
test_str5 = '''this comma’s represented by multiple bytes\n'''
test_str6 = '''why do emojis 💨 💨 💨 seem to break TextCtrl.SetStyle 💨 💨 💨\n'''
test_str7 = '''more single byte characters\n'''
test_str8 = '''to demonstrate the issue.'''

def main():
    main = TestFrame()
    main.Show()
    app.MainLoop()

class TestFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="TestFrame")
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel = TestPanel(self)
        sizer.Add(self.panel, proportion=1, flag=wx.EXPAND)

class TestPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.text = wx.TextCtrl(self, wx.ID_ANY, style=(wx.TE_MULTILINE|wx.TE_RICH|wx.TE_READONLY))
        self.raw_text = ""
        self.styles = []
        self.AddColorText(test_str1, wx.BLUE)
        self.AddColorText(test_str2, wx.RED)
        self.AddColorText(test_str3, wx.BLUE)
        self.AddColorText(test_str4, wx.RED)
        self.AddColorText(test_str5, wx.BLUE)
        self.AddColorText(test_str6, wx.RED)
        self.AddColorText(test_str7, wx.BLUE)
        self.AddColorText(test_str8, wx.RED)
        for s in self.styles:
            self.text.SetStyle(s[0], s[1], s[2])
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.text, proportion=1, flag=wx.EXPAND)
        self.SetSizer(sizer)

    def AddColorText(self, text, wx_color):
        start = self.text.GetLastPosition()
        self.text.AppendText(text)
        end = self.text.GetLastPosition()
        self.styles.append([start, end, wx.TextAttr(wx_color)])

if __name__ == "__main__":
    main()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM