0

我正在尝试创建一个全屏窗口,其中带有很长标签的 statictextctrl 被布置在一个表格中,当它们很多时带有垂直滚动条。我需要做的是根据每个 statictextctrl 可用的宽度(基于每行的 textctrl 数量和 hpad)包装标签。不幸的是,当有一个很长的单词时,我无法使其工作,单词没有换行,即使我明确地将 textctrl 样式设置为wx.TE_BESTWRAP(无论如何这是默认设置,但我认为值得尝试)。知道如何实现这一目标吗?

import wx
import wx.lib.scrolledpanel as scrolled

class MyPanel(scrolled.ScrolledPanel):

    def __init__(self, parent):
        scrolled.ScrolledPanel.__init__(self, parent, -1)
        
        
        ## Configuring the panel
        self.SetAutoLayout(1)
        self.SetupScrolling()
        self.SetScrollRate(1,40)
        
    # needs to be called after main window's laytou, so its size is actually
    # known and can be used to compute textctrl's width
    def Build(self):
        labelPerRow=7
        hgap = 40
        vgap = 20
        label_width=int(self.GetClientSize()[0]/labelPerRow)-hgap
        print("label width", label_width)
        
        grid_sizer = wx.FlexGridSizer(labelPerRow,  vgap, hgap)
        
        self.SetSizer(grid_sizer)
        i=0
        for label in range(200)   :
            label = "very long title withaverybigwordthatdoesntfitonasinglelinesoitsquitehardtomanagewordwrap"+str(i)
            title = wx.StaticText(self,
                                  label=label,
                                  style=wx.ALIGN_CENTRE_HORIZONTAL |
                                        wx.TE_BESTWRAP)
            
            title.Wrap(int(label_width))
            grid_sizer.AddMany([(title)])
            i = i+1
            
        self.Layout()

class MyFrame(wx.Frame):

    def __init__(self):

        wx.Frame.__init__(self, None, title="Test",  style=wx.NO_BORDER)
        self.Maximize(True)
        frameSizer = wx.BoxSizer()
        p = MyPanel(self)
        frameSizer.Add(p, 1, wx.EXPAND)
        self.SetSizer(frameSizer)
        self.Layout()
        p.Build()


app = wx.App(0)
frame = MyFrame()
frame.Show()
app.MainLoop()
4

1 回答 1

0

这是我想出的。我希望它对其他人有用。'\n'基本上,它使用或计算分割的每个组合' ',保持宽度适合的标签和最小高度。如果不适合换行,则使用宽度最小的标签,并在末尾删除适当的字符,用省略号替换它们,以使标签适合。它可以改进,例如使用最大行数或更多文本对齐选项,但到目前为止它被证明是有效的。请注意,如果展开的标签很长(组合数是空格数的指数),则计算所有组合可能会很耗时。


class WrappedStaticText(wx.StaticText):
    """
    A StaticText with advanced wrapping management. Every possible wrapping
    of the label is computed (can be CPU-consuming if label has many spaces) and
    the best one is selected, i.e. the one that respects the wrapping rule and
    uses the smallest number of rows. If no wrapping is possible, an ellipsed
    label is used, based on the least wide and high combination.
    """
    def __init__(self, parent, label, width, font, center=False):
        """
        Constructor.
        
        * `parent`: wx parent,
        * `label`: text to be displayed wrapped
        * `width`: width on which `label` is wrapped
        * `font`: Font used to compute xrapping and display the wrapped label
        * `center`: Whether the wrapped label is centered
        """
        super().__init__(parent)
        
        self.center = center
        self.unwrappedLabel = label
        self.wrappedWidth = width
        self.maxRows = max_rows
        
        self.SetFont(font)
        self.SetLabel(label)
        self.Bind(wx.EVT_PAINT, self.OnPaint) 

    def SetLabel(self, label):
        """
        Changes the text's label. Recompute the wrapped label
        
        * `label`: new label to wrap and display
        """
        dc = wx.ScreenDC()
        dc.SetFont(self.GetFont())
        
        label_split = label.split()
        
        ## For wrapping label
        best_height=sys.maxsize
        best_height=sys.maxsize
        best_label = None
        
        ## For ellipsis if wrapping is not possible because of too long word
        best_unwrapped_height=sys.maxsize
        best_unwrapped_width=sys.maxsize
        best_unwrapped_label = None
        labels = self.get_wrappings(label_split[0], label_split[1:])
        
        ## Search for the best label
        for l in labels:
            w,h = dc.GetMultiLineTextExtent(l)
            
            ## Update best wrapping
            if w <= self.wrappedWidth:
                if h < best_height:
                    best_label  = l
                    best_height = h
            ## Update best ellipsis in case of wrapping fail
            elif w == best_unwrapped_width:
                 if h < best_unwrapped_height:
                        best_unwrapped_label  = l
                        best_unwrapped_height = h
            elif w < best_unwrapped_width:
                        best_unwrapped_label  = l
                        bes_unwrappedt_widtht = w
                        best_unwrapped_height = h
        
        ## In case of wrapping fail, search for the best ellipsed label
        if best_label == None:
            while best_unwrapped_width > self.wrappedWidth:
                best_unwrapped_label = best_unwrapped_label[:-1]
                best_unwrapped_width = \
                    dc.GetMultiLineTextExtent(best_unwrapped_label+"…&quot;)[0]
            best_label = best_unwrapped_label+"…&quot;
        super().SetLabel(best_label)
        print(best_label)

    def SetFont(self, font):
        """
        Changes the text's font. Recompute the wrapped label
        
        * `font`: new font to wrap and display the text's label with
        """
        super().SetFont(font)
        self.SetLabel(self.unwrappedLabel)

    def get_wrappings(self, text, words):
        """
        Recursively compute all combinations of text splitting using either a
        space or a new line. May be time-consuming if label has too many spaces.
        
        * `text`: beginning of the full label, already wrapped
        * `words`: list of remaining words to be added to `text`
        
        * yields: every combinations of `text` and remaining words, concatenated
          either with `'\n'` ot `' '`
        """

        if len(words) == 0:
            ## If there is no space at all in the unwrapped label
            yield text
        elif len(words) == 1:
            ## if it's the last word of the text, just yield the two versions
            yield text+" "  + words[0]
            yield text+"\n" + words[0]
        else:
            ## try the two spliting combinations, and recursively compute the
            #  wrapping for the remaining text
            yield from self.get_wrappings(text+" "  + words[0], words[1:])
            yield from self.get_wrappings(text+"\n" + words[0], words[1:])

    
    def OnPaint(self, event):
        """
        Paint callback. Overrides the paint event callback so that centered text
        painting renders the right way with wrapped text.
        """
        dc = wx.PaintDC(self) 

        curr_size   = self.GetSize()
        curr_width  = curr_size[0]
        curr_height = curr_size[1]
        
        label_h = 0
        for line in self.GetLabel().split("\n"):
            text_w, text_h = dc.GetTextExtent(line)
            if self.center:
                w = curr_width/2 - text_w/2
            else:
                w = 0
            dc.DrawText(line, w, label_h)
            label_h+=text_h


于 2021-03-08T09:49:40.460 回答