Home-Produkte-Testarea-Kontakt-Datenschutz-Aktualisiert: 22-Jul-2013
< Voriger Tag   Nächster Tag >

Montag, 22. Juli 2013

SWB: Links, Listen einrücken, Adresszeile

SimpleWebBrowser unterstützt nun Links, die Listenelemente werden eingerückt und es gibt eine Adresszeile.

Links

Links sind einfach implementiert. Sie werden in blau hervorgehoben und bei einem Mausklick wird die neue Seite geladen. Mouseover, Linkhistory u.ä. werden nicht unterstützt.

Gesammelt werden die Links in SimpleWebBrowser.link[] mit ihrem href-Wert und den zugehörigen Widgets. link.py:

class Link:
    def __init__(self, href=None):
        self.href    = href
        self.widgets = []

    def is_point_in(self, x, y):
        for widget in self.widgets:
            if widget.is_point_in(x, y):
                return True

        return False

simplewebbrowser.py:

class SimpleWebbrowser:
...
    self.links        = []
    self.link_current = None
    self.link_active  = ''
....


def parse_html(self, element):
    ...

    if element.tag == 'a':
        self.link_current = Link(href=element.get('href'))
        self.links.append(self.link_current)

        self.attr_stack.append(self.attr_current)
        self.attr_current = AttributeWidget(fg=(0,0,255,255), bg=(255,255,255,255))
        self.text_box.widgets.append(self.attr_current)

    if element.text != None:
        for word in element.text.split():
            text_widget = TextWidget(word)
            self.text_box.widgets.append(text_widget)

            if self.link_current != None:
                self.link_current.widgets.append(text_widget)

    for child in element:
        self.parse_html(child)
    ...

    if element.tag == 'a':
        self.link_current = None

        self.attr_current = self.attr_stack.pop()
        self.text_box.widgets.append(self.attr_current)

    if element.tail != None:
        for word in element.tail.split():
            text_widget = TextWidget(word)
            self.text_box.widgets.append(text_widget)

            if self.link_current != None:
                self.link_current.widgets.append(text_widget)

Bei einem Mausklick wird überprüft, ob der Mauszeiger in einem der Widgets liegt und der zugehörige Link aufgerufen. simplewebbrowser.py:

def on_mouse_up(self, event):
    for link in self.links:
        if link.is_point_in(event.pos[0] - self.text_box.left + self.text_box.scroll_left,
                            event.pos[1] - self.text_box.top  + self.text_box.scroll_top):
            self.load(link.href)
            break

Dabei müssen relative Links wie href="../path/to/page.html zu absoluten konvertiert werden, in dem sie mit der URL der gerade angezeigten Seite verknüpft werden. Genau dies erledigt urljoin() von der Python-urllib. url_join.py:

from urllib.parse import urljoin

print(urljoin('http://www.example.com/a/b/c/page.html', '../new_path/to/new_page.html'))

Optimierung: Kein Zeichnen bei Mausbewegungen

Außerdem wird zur Optimierung, bei Mausbewegungen und unbekannten Tastendrücken, die Darstellung nun nicht mehr neu gezeichnet. Hätte ich mal gleich als erstes machen sollen - durch diese Optimierung reagiert SimpleWebBrowser wesentlich schneller und angenehmer. simplewebbrowser.py:

def run(self):
    do_redraw = True

    while True:
        if do_redraw:
            self.draw(self.g)
            pygame.display.flip()

        do_redraw = True
        event = pygame.event.wait()

        ...

        if event.type == pygame.MOUSEMOTION:
            do_redraw = False
Download: Revision 138
svn checkout --revision 138 https://svn.sven-drieling.de/repos/trunk/playground/webbrowser/ yd-playground-webbrowser-138

Listen einrücken

Das Einrücken von Listenelementen ist mit Hilfe des Indent-Attributs implementiert. indent_widget.py:

class IndentWidget(Widget):
    def __init__(self, indent=0):
        super().__init__(0, 0, 0, 0)

        self.indent = indent

    def draw(self, g, left, top):
        pass

Beim Anfang einer Liste wird Indent(32) eingefügt und am Ende diese Einrückung mit Indent(-32) wieder rückgängig gemacht. simplewebbrowser.py:

def parse_html(self, element):
    ...

    if element.tag in ['ul', 'ol']:
        self.text_box.widgets.append(IndentWidget(32))

    if element.text != None:
        for word in element.text.split():
    ...

    if element.tag in ['ul', 'ol']:
        self.text_box.widgets.append(IndentWidget(-32))
    ...

layout() berücksichtigt diesen Wert bei der Positionsberechnung der Widgets. Diese ist durch die if isinstance(widget, IndentWidget)-Abfragen nicht schön aber einfach zu implementieren und funktioniert erst einmal. textbox.py:

def layout(self, g):
    ...

    try:
        widget = next(widgetsIter)
        ...

        if isinstance(widget, IndentWidget):
            self.indent += widget.indent

        while True:
            # First widget in line
            widget.left = self.indent
            widget.top  = self.text_height

            line_width  = self.indent + widget.width
            line_height = widget.height


            # Fill line with following widgets
            widget = next(widgetsIter)
            ...
            if isinstance(widget, IndentWidget):
                self.indent += widget.indent
                line_width  += widget.indent
            ...

            while line_width + self.WORD_GAP + widget.width < self.width:
                ...

                if isinstance(widget, IndentWidget):
                    self.indent += widget.indent
                    line_width  += widget.indent
                widget.left = self.left + line_width
                widget.top  = self.text_height
                ...

Mit den funktionierenden Links und eingerückten Listen hat das schon etwas von einem HTML 2.0-Webbrowser :-)

Download: Revision 140
svn checkout --revision 140 https://svn.sven-drieling.de/repos/trunk/playground/webbrowser/ yd-playground-webbrowser-140

Adresszeile

Die Adresszeile zur Anzeige und Eingabe des aktuellen Links ist das text_field_widget.py. Weil es innerhalb des Fensters nun zwei Widgets gibt, die Eingaben verarbeiten, hat sich einiges am Aufbau von simplewebbrowser.py verändert.

Wichtigste Änderung ist die Verwaltung des gerade aktiven Widgets, das die Eingabe zugeschickt bekommt (on_mouse_up(), on_key_down(), ...).

if event.type == pygame.MOUSEBUTTONDOWN:
    if self.address_bar.is_point_in(event.pos[0], event.pos[1]):
        new_active_widget = self.address_bar
    else:
        new_active_widget = self

    if new_active_widget != self.active_widget:
        self.active_widget.on_blur()
        self.active_widget.draw(self.g)

        self.active_widget = new_active_widget

        self.active_widget.on_focus()
        self.active_widget.draw(self.g)

        pygame.display.flip()

    self.active_widget.on_mouse_down(event)

if event.type == pygame.MOUSEBUTTONUP:
    self.active_widget.on_mouse_up(event)

Weil die TextBox derzeit kein Widget ist und z.B. das Sammeln der Links derzeit noch in SimpleWebbrowser und nicht on der TextBox erfolgt, ist das alles im Moment noch etwas durcheinandergewirbelt.

Download: Revision 145
svn checkout --revision 145 https://svn.sven-drieling.de/repos/trunk/playground/webbrowser/ yd-playground-webbrowser-145

[Direktlink]

< Voriger Tag   Nächster Tag >

  RSS V0.91

<Juli 2013 >
01020304050607
08091011121314
15161718192021
22232425262728
293031    

Home-Produkte-Testarea-Kontakt-Datenschutz-Aktualisiert: 22-Jul-2013
(C) 2000-2018 by Sven Drieling