0

我正在 Python 3.7.3 中使用 wxPython 4.0.4 开发应用程序,但在尝试为 wx.TextCtrl 中的 UTF-8 文本着色时遇到了问题。基本上,似乎某些字符在 wxPython 中被错误地计算,尽管它们在 Python 中被正确计算。

我最初认为所有多字节字符都被错误计数,但是,我下面的示例代码表明情况并非如此。这似乎是 wx.TextCtrl.SetStyle 函数中的一个问题。

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()

在此处输入图像描述

4

2 回答 2

0

看来我的问题是使用 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()
于 2020-09-05T17:45:35.577 回答
0

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 中超过了两个字节。而 wxPython 无法解释这一点:如果 wxPython 将startend计数器直接传递给底层的 Windows 函数,那么它们将在 Emoji 之后关闭,因为给你的值是 Unicode 代码点,而 Windows 期望的值是 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 问题跟踪器

于 2020-06-27T16:46:11.830 回答