Auto wrap and newlines in wxPython grid

2019-05-12 16:50发布

I want to implement a grid with the cells that have the following behaviour:

  1. cell text should be wrapped if it doesn't fit to the cell

  2. newlines (\n) in the cell text should be processed as well

i.e. the same behaviour as in table editors like MS Excel, OO Calc, etc. when you enable the 'wrap words' option for cells.

I'm trying to do this as follows:

import wx 
import wx.grid 

class MyGrid(wx.grid.Grid): 

    def __init__(self, parent = None, style = wx.WANTS_CHARS): 

        wx.grid.Grid.__init__(self, parent, -1, style = style) 

        self.CreateGrid(10, 10) 

        self.editor = wx.grid.GridCellAutoWrapStringEditor() 
        self.SetDefaultEditor(self.editor)

        self.SetDefaultRenderer(wx.grid.GridCellAutoWrapStringRenderer()) 

        self.SetCellValue(0, 0, "Line1\nLine2\nLine3") 
        self.SetRowSize(0, 100) 


class MyFrame(wx.Frame): 

    def __init__(self, parent = None, title = "Multiline"): 

        wx.Frame.__init__(self, parent, -1, title) 

        self.Bind(wx.EVT_CHAR_HOOK, self.on_frame_char_hook) 

        panel = wx.Panel(self) 

        vbox = wx.BoxSizer(wx.VERTICAL) 
        panel.SetSizer(vbox) 

        grid = MyGrid(panel) 
        vbox.Add(grid, 1, wx.EXPAND | wx.ALL, 5) 
        self.grid = grid 

        btn_exit = wx.Button(panel, -1, "Exit") 
        vbox.Add(btn_exit, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 10) 

    #Proceed CTRL+ENTER as newline in the cell editor
    def on_frame_char_hook(self, event): 

        if event.CmdDown() and event.GetKeyCode() == wx.WXK_RETURN: 
            if self.grid.editor.IsCreated(): 
                self.grid.editor.StartingKey(event) 
            else: 
                event.Skip 
        else: 
            event.Skip()


if __name__ == "__main__": 
    app = wx.PySimpleApp() 
    f = MyFrame() 
    f.Center() 
    f.Show() 
    app.MainLoop()

But this code doesn't work as expected - newlines processed correctly in the cell editor, but ignored in the cell renderer. If I remove the self.SetDefaultRenderer(wx.grid.GridCellAutoWrapStringRenderer()) then newlines processed correcly both in the editor and renderer, but obviously auto wrapping in the renderer doesn't work.

Does anybody know how to solve this?

1条回答
贼婆χ
2楼-- · 2019-05-12 17:13

Solved this problem by writing a custom renderer:

from wx.lib import wordwrap
import wx.grid


class CutomGridCellAutoWrapStringRenderer(wx.grid.PyGridCellRenderer):   
    def __init__(self): 
        wx.grid.PyGridCellRenderer.__init__(self)

    def Draw(self, grid, attr, dc, rect, row, col, isSelected):
        text = grid.GetCellValue(row, col)
        dc.SetFont( attr.GetFont() ) 
        text = wordwrap.wordwrap(text, grid.GetColSize(col), dc, breakLongWords = False)
        hAlign, vAlign = attr.GetAlignment()       
        if isSelected: 
            bg = grid.GetSelectionBackground() 
            fg = grid.GetSelectionForeground() 
        else: 
            bg = attr.GetBackgroundColour()
            fg = attr.GetTextColour() 
        dc.SetTextBackground(bg) 
        dc.SetTextForeground(fg)
        dc.SetBrush(wx.Brush(bg, wx.SOLID))
        dc.SetPen(wx.TRANSPARENT_PEN)
        dc.DrawRectangleRect(rect)            
        grid.DrawTextRectangle(dc, text, rect, hAlign, vAlign)

    def GetBestSize(self, grid, attr, dc, row, col): 
        text = grid.GetCellValue(row, col)
        dc.SetFont(attr.GetFont())
        text = wordwrap.wordwrap(text, grid.GetColSize(col), dc, breakLongWords = False)
        w, h, lineHeight = dc.GetMultiLineTextExtent(text)                   
        return wx.Size(w, h)        

    def Clone(self): 
        return CutomGridCellAutoWrapStringRenderer()
查看更多
登录 后发表回答