如何在调整大小期间将 wx.html.HtmlWindow 保持在同一位置(不闪烁)?

发布于 2024-08-13 18:30:50 字数 2510 浏览 7 评论 0原文

我希望能够调整 html 窗口的大小但保持滚动位置。这个例子几乎可以工作,但是会闪烁。
测试:
- 使用“加载 Html 文件”按钮加载大小合适的 html 文件
- 向下滚动
- 调整窗口大小。

窗口通过调整大小保持相同的位置,但它闪烁得很厉害。我认为,htmlwindow 的代码每次调整大小时都会将滚动位置重置为 0。我想阻止它重新绘制,直到滚动位置在 post_resize 函数中固定为止。

我尝试过各种冻结/解冻组合并尝试加入绘画事件,但没有成功。建议?

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

I want to be able to resize an html window but keep the scroll position. This example almost works, but flickers.
To test:
- load a good sized html file using the Load Html File button
- scroll down
- resize the window.

The window keeps its same position through resizes, but it flickers horribly. The code for htmlwindow is resetting the scroll position to 0 each resize, I think. I'd like to keep it from redrawing until the scroll position is fixed in the post_resize function.

I've tried various combinations of freeze/thaw and trying to hook into the paint events, but have had no success. Suggestions?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

最美不过初阳 2024-08-20 18:30:50

我使用 ScrolledPanel(在调整大小时不会重置滚动条的位置)和直接渲染文本来回避这个问题。 (我只使用 html 面板对文本进行颜色编码)。

结果类:

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

I sidestepped the problem using a ScrolledPanel (which doesn't reset its scrollsbars' positions on resize) and direct rendering of the text. (I was only using the html panel to color-code text).

The resulting class:

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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文