¿Cómo puedo mantener un wx.html.HtmlWindow en la misma posición durante el cambio de tamaño (sin parpadeo)?

StackOverflow https://stackoverflow.com/questions/1871405

Pregunta

Quiero ser capaz de cambiar el tamaño de una ventana HTML, pero mantener la posición de desplazamiento. Este ejemplo casi funciona, pero parpadea.
Para probar:
- cargar un archivo html buen tamaño con el botón Cargar archivo HTML
- desplazarse hacia abajo
-. Redimensionar la ventana

La ventana mantiene su misma posición a través cambia el tamaño, sino que parpadea horriblemente. El código para HTMLWindow está restableciendo la posición de desplazamiento para cambiar el tamaño de cada uno 0, creo. Me gustaría evitar que volver a dibujar hasta que la posición de desplazamiento se fija en la función post_resize.

He intentado varias combinaciones de congelación / descongelación y tratando de enganchar en los eventos de pintura, pero no han tenido éxito. Sugerencias?

import wx
import  wx.html

_POST_RESIZE_EVENT = wx.NewEventType()

class _PostResizeEvent(wx.PyEvent):
    def __init__(self, pos):
        wx.PyEvent.__init__(self)
        self.SetEventType(_POST_RESIZE_EVENT)
        self.pos = pos

def EVT_POST_RESIZE(win, func):
    win.Connect(-1, -1, _POST_RESIZE_EVENT, func)


class MyHtmlPanel(wx.Panel):
    """
    class MyHtmlPanel inherits wx.Panel and adds a button and HtmlWindow
    """
    def __init__(self, parent, id):
        # default pos is (0, 0) and size is (-1, -1) which fills the frame
        wx.Panel.__init__(self, parent, id)
        self.SetDoubleBuffered(True)
        self.SetBackgroundColour("yellow")
        self.html1 = wx.html.HtmlWindow(self, id, pos=(0,30), size=(602,310))

        self.btn1 = wx.Button(self, -1, "Load Html File", pos=(0,0))
        self.btn1.Bind(wx.EVT_BUTTON, self.OnLoadFile)

        self.btn2 = wx.Button(self, -1, "Clear Page", pos=(120,0))
        self.btn2.Bind(wx.EVT_BUTTON, self.OnClearPage)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.btn1, 0)
        sizer.Add(self.btn2, 0)
        sizer.Add(self.html1, 1, wx.EXPAND)
        self.SetSizer(sizer)

        self.html1.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        EVT_POST_RESIZE(self.html1, self.post_resize)
        self.hist=0

    def OnScroll(self, evt):
        self.hist = self.html1.GetViewStart()[1]
        evt.Skip()

    def OnSize(self, evt):
        wx.PostEvent(self.html1, _PostResizeEvent(self.hist))
        evt.Skip()

    def post_resize(self, evt):
        self.html1.Scroll(0, evt.pos)

    def OnLoadFile(self, event):
        dlg = wx.FileDialog(self, wildcard = '*.html', style=wx.OPEN)
        if dlg.ShowModal():
            path = dlg.GetPath()
            self.html1.LoadPage(path)
        dlg.Destroy()

    def OnClearPage(self, event):
        self.html1.SetPage("")


app = wx.PySimpleApp()
# create a window/frame, no parent, -1 is default ID, title, size
frame = wx.Frame(None, -1, "HtmlWindow()", size=(610, 380))
# call the derived class, -1 is default ID
MyHtmlPanel(frame,-1)
# show the frame
frame.Show(True)
# start the event loop
app.MainLoop()
¿Fue útil?

Solución

Yo esquivé el problema utilizando un ScrolledPanel (la cual no restablece sus posiciones scrollsbars' en el cambio de tamaño) y la representación directa del texto. (Que sólo estaba usando el panel html al texto de código de color).

La clase resultante:

import wx
import wx.lib.scrolledpanel as scrolledpanel


class ScrollableText(scrolledpanel.ScrolledPanel):
    def __init__(self, parent, id=-1, text=[[('black', 'adsf')]]):
        scrolledpanel.ScrolledPanel.__init__(self, parent, id)

        self.SetBackgroundColour('white')
        self.SetVirtualSize((1500, 1500))
        self.SetupScrolling(True, True)

        # a list of lists of (color, string) tuples
        self.Bind(wx.EVT_PAINT, self.redraw)
        self.Bind(wx.EVT_SIZE, self.resize)

        # setup the font
        self.font = wx.Font(10, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.NORMAL)
        if not self.font.IsFixedWidth():
            self.font = wx.Font(10, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)
        assert self.font.IsFixedWidth()

        self.text = text
        self.set_longest_line()

    def set_text(self, text):
        self.text = text
        self.set_longest_line()
        self.resize(None)
        self.Refresh()

    def set_longest_line(self):
        if len(self.text) > 0:
            self.longest_line = max([sum([len(s) for c, s in l]) for l in self.text])
        else:
            self.longest_line = 1

    def draw_lines(self, DC, start, end, lineheight):
        mw = 0
        DC.SetFont(self.font)
        for idx in range(max(0, start), min(end, len(self.text))):
            l = self.text[idx]
            combined = "".join([s[1] for s in l])
            extents = [0] + DC.GetPartialTextExtents(combined)
            for str in l:
                DC.SetTextForeground(str[0])
                DC.DrawText(str[1], extents[0], idx * lineheight)
                extents = extents[len(str[1]):]

    def resize(self, evt):
        DC = wx.ClientDC(self)
        DC.SetFont(self.font)

        # find line width and  height
        extent = DC.GetFullTextExtent('X'*self.longest_line)
        lineheight = extent[1]
        maxwidth = extent[0]

        # set virtual area
        vsize = (maxwidth, len(self.text) * lineheight)
        if self.GetVirtualSize() != vsize:
            self.SetVirtualSize(vsize)

    def redraw(self, evt):
        DC = wx.PaintDC(self)
        self.PrepareDC(DC)
        extent = DC.GetFullTextExtent('x'*self.longest_line)
        lineheight = extent[1]
        vs = self.GetViewStart()
        ppu = self.GetScrollPixelsPerUnit()
        ri = wx.RegionIterator(self.GetUpdateRegion())
        mmin, mmax = len(self.text), 0
        while ri:
            rect = ri.GetRect()
            # find the lines that need rendering
            min_y = rect[1] + vs[1] * ppu[1]
            max_y = rect[1] + rect[3] + vs[1] * ppu[1]
            min_line = int(min_y / lineheight) - 1
            max_line = int(max_y / lineheight) + 2
            mmin = min(min_line, mmin)
            mmax = max(max_line, mmax)
            ri.Next()
        self.draw_lines(DC, mmin, mmax, lineheight)



if __name__ == '__main__':
    class MainFrame(wx.Frame):
        def __init__(self, parent):
            wx.Frame.__init__(self, parent, -1)
            t = [[('black', l[:len(l)/2]), ('red', l[len(l)/2:])] for l in open('scrollable_text.py')]
            self.scrollable = ScrollableText(self, -1, t)

    app = wx.PySimpleApp(None,-1)
    frame = MainFrame(parent=None)
    frame.Show()
    app.MainLoop()
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top