0.3.4 removed tkhtmlview from deps

This commit is contained in:
DESKTOP-JVKHS7I\Adam 2020-05-20 16:49:01 +05:30
parent 3e96fbed2c
commit 168e902e36
7 changed files with 822 additions and 5 deletions

View File

@ -1,2 +1,2 @@
from fishy.__main__ import main
__version__ = "0.3.3"
__version__ = "0.3.4"

View File

@ -2,11 +2,12 @@ import time
from tkinter import *
from tkinter import messagebox
from tkinter.ttk import *
from tkhtmlview import HTMLLabel
from fishy import web
import typing
from fishy.libs.tkhtmlview import HTMLLabel
if typing.TYPE_CHECKING:
from . import GUI

1
fishy/libs/__init__.py Normal file
View File

@ -0,0 +1 @@
from . import tkhtmlview

View File

@ -0,0 +1,129 @@
"""
tkinter HTML text widgets
"""
import sys
import tkinter as tk
from . import html_parser
VERSION = "0.1.0.post1"
class _ScrolledText(tk.Text):
# ----------------------------------------------------------------------------------------------
def __init__(self, master=None, **kw):
self.frame = tk.Frame(master)
self.vbar = tk.Scrollbar(self.frame)
kw.update({'yscrollcommand': self.vbar.set})
self.vbar.pack(side=tk.RIGHT, fill=tk.Y)
self.vbar['command'] = self.yview
tk.Text.__init__(self, self.frame, **kw)
self.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
text_meths = vars(tk.Text).keys()
methods = vars(tk.Pack).keys() | vars(tk.Grid).keys() | vars(tk.Place).keys()
methods = methods.difference(text_meths)
for m in methods:
if m[0] != '_' and m != 'config' and m != 'configure':
setattr(self, m, getattr(self.frame, m))
def __str__(self):
return str(self.frame)
class HTMLScrolledText(_ScrolledText):
# ----------------------------------------------------------------------------------------------
"""
HTML scrolled text widget
"""
def __init__(self, *args, html=None, **kwargs):
# ------------------------------------------------------------------------------------------
super().__init__(*args, **kwargs)
self._w_init(kwargs)
self.html_parser = html_parser.HTMLTextParser()
if isinstance(html, str):
self.set_html(html)
def _w_init(self, kwargs):
# ------------------------------------------------------------------------------------------
if not 'wrap' in kwargs.keys():
self.config(wrap='word')
if not 'background' in kwargs.keys():
if sys.platform.startswith('win'):
self.config(background='SystemWindow')
else:
self.config(background='white')
def fit_height(self):
# ------------------------------------------------------------------------------------------
"""
Fit widget height to wrapped lines
"""
for h in range(1, 4):
self.config(height=h)
self.master.update()
if self.yview()[1] >= 1:
break
else:
self.config(height=0.5 + 3 / self.yview()[1])
def set_html(self, html, strip=True):
# ------------------------------------------------------------------------------------------
"""
Set HTML widget text. If strip is enabled (default) it ignores spaces and new lines.
"""
prev_state = self.cget('state')
self.config(state=tk.NORMAL)
self.delete('1.0', tk.END)
self.tag_delete(self.tag_names)
self.html_parser.w_set_html(self, html, strip=strip)
self.config(state=prev_state)
class HTMLText(HTMLScrolledText):
# ----------------------------------------------------------------------------------------------
"""
HTML text widget
"""
def _w_init(self, kwargs):
# ------------------------------------------------------------------------------------------
super()._w_init(kwargs)
self.vbar.pack_forget()
def fit_height(self):
# ------------------------------------------------------------------------------------------
super().fit_height()
# self.master.update()
self.vbar.pack_forget()
class HTMLLabel(HTMLText):
# ----------------------------------------------------------------------------------------------
"""
HTML label widget
"""
def _w_init(self, kwargs):
# ------------------------------------------------------------------------------------------
super()._w_init(kwargs)
if not 'background' in kwargs.keys():
if sys.platform.startswith('win'):
self.config(background='SystemButtonFace')
else:
self.config(background='#d9d9d9')
if not 'borderwidth' in kwargs.keys():
self.config(borderwidth=0)
if not 'padx' in kwargs.keys():
self.config(padx=3)
def set_html(self, *args, **kwargs):
# ------------------------------------------------------------------------------------------
super().set_html(*args, **kwargs)
self.config(state=tk.DISABLED)

View File

@ -0,0 +1,688 @@
"""
HTML parser
"""
import os
import webbrowser
import tkinter as tk
from tkinter import font
from copy import deepcopy
from PIL import Image, ImageTk
from html.parser import HTMLParser
from collections import OrderedDict
import requests
from io import BytesIO
# __________________________________________________________________________________________________
class Defs:
DEFAULT_TEXT_FONT_FAMILY = ("Segoe ui", "Calibri", "Helvetica", "TkTextFont")
FONT_SIZE = 14
PREFORMATTED_FONT_FAMILY = ("Courier", "DejaVu Sans Mono", "TkFixedFont")
HEADINGS_FONT_SIZE = {
'h1': 32,
'h2': 24,
'h3': 18,
'h4': 16,
'h5': 13,
'h6': 10,
}
class HTML:
# ----------------------------------------------------------------------------------------------
"""
List of supported HTML tags and attrs
"""
class Tag():
BR = 'br'
UL = 'ul'
OL = 'ol'
LI = 'li'
IMG = 'img'
A = 'a'
B = 'b'
STRONG = 'strong'
I = 'i'
EM = 'em'
U = 'u'
MARK = 'mark'
SPAN = 'span'
DIV = 'div'
P = 'p'
PRE = 'pre'
CODE = 'code'
H1 = 'h1'
H2 = 'h2'
H3 = 'h3'
H4 = 'h4'
H5 = 'h5'
H6 = 'h6'
class Attrs():
STYLE = 'style'
HREF = 'href'
SRC = 'src'
WIDTH = 'width'
HEIGHT = 'height'
TYPE = 'type'
class TypeOrderedList():
_1 = '1'
a = 'a'
A = 'A'
class Style():
COLOR = 'color'
BACKGROUD_COLOR = 'background-color'
FONT_FAMILY = 'font-family'
FONT_SIZE = 'font-size'
TEXT_ALIGN = 'text-align'
TEXT_DECORATION = 'text-decoration'
class StyleTextDecoration():
UNDERLINE = 'underline'
LINE_THROUGH = 'line-through'
HEADING_TAGS = (
Tag.H1,
Tag.H2,
Tag.H3,
Tag.H4,
Tag.H5,
Tag.H6,
)
TEXT_ALIGN_TAGS = HEADING_TAGS + (
Tag.UL,
Tag.OL,
Tag.LI,
Tag.DIV,
Tag.P,
Tag.PRE,
Tag.CODE,
)
NEW_LINE_TAGS = HEADING_TAGS + (
Tag.UL,
Tag.OL,
Tag.DIV,
Tag.P,
Tag.PRE,
Tag.CODE,
)
STYLE_TAGS = TEXT_ALIGN_TAGS + (
Tag.A,
Tag.B,
Tag.STRONG,
Tag.I,
Tag.EM,
Tag.U,
Tag.MARK,
Tag.SPAN,
)
# --------------------------------------------------------------------------------------------------
# Text widget defs
class WCfg():
KEY = "config"
BACKGROUND = "background"
FOREGROUND = "foreground"
JUSTIFY = "justify"
TABS = "tabs"
class Fnt():
KEY = "font"
FAMILY = "family"
SIZE = "size"
WEIGHT = "weight"
SLANT = "slant"
UNDERLINE = "underline"
OVERSTRIKE = "overstrike"
class Bind():
KEY = "bind"
LINK = "link"
IMAGE = "image"
class WTag():
START_INDEX = "start_index"
END_INDEX = "end_index"
DEFAULT_STACK = {
WCfg.KEY: {
WCfg.BACKGROUND: [],
WCfg.FOREGROUND: [("__DEFAULT__", "black")],
WCfg.JUSTIFY: [("__DEFAULT__", 'left')],
WCfg.TABS: [("__DEFAULT__", ())],
},
Fnt.KEY: {
Fnt.FAMILY: [],
Fnt.SIZE: [("__DEFAULT__", Defs.FONT_SIZE)],
Fnt.WEIGHT: [("__DEFAULT__", 'normal')],
Fnt.SLANT: [("__DEFAULT__", 'roman')],
Fnt.UNDERLINE: [("__DEFAULT__", False)],
Fnt.OVERSTRIKE: [("__DEFAULT__", False)],
},
Bind.KEY: {
Bind.LINK: [("__DEFAULT__", None)],
},
}
# __________________________________________________________________________________________________
# functions
def get_existing_font(font_families):
# ------------------------------------------------------------------------------------------
try:
return next(filter(lambda f: f.lower() in (f.lower() for f in font.families()), font_families))
except:
return "TkTextFont"
# __________________________________________________________________________________________________
# classes
class HLinkSlot():
# ----------------------------------------------------------------------------------------------
def __init__(self, w, tag_name, url):
# ------------------------------------------------------------------------------------------
self._w = w
self.tag_name = tag_name
self.URL = url
def call(self, event):
# ------------------------------------------------------------------------------------------
webbrowser.open(self.URL)
self._w.tag_config(self.tag_name, foreground="purple")
def enter(self, event):
# ------------------------------------------------------------------------------------------
self._w.config(cursor="hand2")
def leave(self, event):
# ------------------------------------------------------------------------------------------
self._w.config(cursor="")
class ListTag():
# ----------------------------------------------------------------------------------------------
def __init__(self, ordered: bool, list_type=None):
# ------------------------------------------------------------------------------------------
self.ordered = ordered
self.type = list_type
self.index = 0
def add(self):
# ------------------------------------------------------------------------------------------
if self.ordered:
self.index += 1
def line_index(self):
# ------------------------------------------------------------------------------------------
if self.ordered:
if self.type == HTML.TypeOrderedList._1:
return str(self.index)
elif self.type == HTML.TypeOrderedList.a:
return self._index_to_str(self.index).lower()
elif self.type == HTML.TypeOrderedList.A:
return self._index_to_str(self.index).upper()
else:
return chr(8226)
def _index_to_str(self, index):
# ------------------------------------------------------------------------------------------
prefix = ""
if index > 26:
prefix = self._index_to_str(index // 26)
index = index % 26
return prefix + chr(0x60 + index)
class HTMLTextParser(HTMLParser):
# ----------------------------------------------------------------------------------------------
def __init__(self):
# ------------------------------------------------------------------------------------------
super().__init__()
# set list tabs
self.cached_images = {}
self.DEFAULT_TEXT_FONT_FAMILY = get_existing_font(Defs.DEFAULT_TEXT_FONT_FAMILY)
self.PREFORMATTED_FONT_FAMILY = get_existing_font(Defs.PREFORMATTED_FONT_FAMILY)
def _parse_attrs(self, attrs):
# ------------------------------------------------------------------------------------------
attrs_dict = {
HTML.Attrs.STYLE: {},
HTML.Attrs.HREF: None,
HTML.Attrs.SRC: None,
HTML.Attrs.WIDTH: None,
HTML.Attrs.HEIGHT: None,
HTML.Attrs.TYPE: None,
}
for k, v in attrs:
k = k.lower()
if k == HTML.Attrs.STYLE:
for p in v.split(";"):
try:
p_key = p.split(":")[0].strip().lower()
p_value = p.split(":")[1].strip().lower()
attrs_dict[HTML.Attrs.STYLE][p_key] = p_value
except:
pass
elif k in (HTML.Attrs.HREF, HTML.Attrs.SRC, HTML.Attrs.WIDTH, HTML.Attrs.HEIGHT, HTML.Attrs.TYPE):
attrs_dict[k] = v
return attrs_dict
def _w_tags_add(self):
# ------------------------------------------------------------------------------------------
tag = {
WCfg.KEY: {},
Fnt.KEY: {},
Bind.KEY: {}
}
for k1 in (WCfg.KEY, Fnt.KEY, Bind.KEY):
for k2 in DEFAULT_STACK[k1]:
tag[k1][k2] = self.stack[k1][k2][-1][1]
self._w_tags[self._w.index("end-1c")] = tag
def _stack_get_main_key(self, key):
# ------------------------------------------------------------------------------------------
if key in WCfg.__dict__.values():
main_key = WCfg.KEY
elif key in Fnt.__dict__.values():
main_key = Fnt.KEY
elif key in Bind.__dict__.values():
main_key = Bind.KEY
else:
raise ValueError("key %s doesn't exists" % key)
return main_key
def _stack_add(self, tag, key, value=None):
# ------------------------------------------------------------------------------------------
main_key = self._stack_get_main_key(key)
if value is None:
# if value is none, add the previous value
value = self.stack[main_key][key][-1][1]
self.stack[main_key][key].append((tag, value))
def _stack_index(self, tag, key):
# ------------------------------------------------------------------------------------------
main_key = self._stack_get_main_key(key)
index = None
for i, v in enumerate(self.stack[main_key][key]):
if v[0] == tag:
index = i
return index
def _stack_pop(self, tag, key):
# ------------------------------------------------------------------------------------------
main_key = self._stack_get_main_key(key)
index = None
if len(self.stack[main_key][key]) > 1:
index = self._stack_index(tag, key)
if index is not None:
return self.stack[main_key][key].pop(index)[1]
def _parse_styles(self, tag, attrs):
# ------------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------- [ COLOR ]
if HTML.Style.COLOR in attrs[HTML.Attrs.STYLE].keys():
self._stack_add(tag, WCfg.FOREGROUND, attrs[HTML.Attrs.STYLE][HTML.Style.COLOR])
elif tag == HTML.Tag.A and attrs[HTML.Attrs.HREF]:
self._stack_add(tag, WCfg.FOREGROUND, "blue")
else:
self._stack_add(tag, WCfg.FOREGROUND)
# ---------------------------------------------------------------------- [ BACKGROUD_COLOR ]
if HTML.Style.BACKGROUD_COLOR in attrs[HTML.Attrs.STYLE].keys():
self._stack_add(tag, WCfg.BACKGROUND, attrs[HTML.Attrs.STYLE][HTML.Style.BACKGROUD_COLOR])
elif tag == HTML.Tag.MARK:
self._stack_add(tag, WCfg.BACKGROUND, "yellow")
else:
self._stack_add(tag, WCfg.BACKGROUND)
# -------------------------------------------------------------------------- [ FONT_FAMILY ]
# font family
if HTML.Style.FONT_FAMILY in attrs[HTML.Attrs.STYLE].keys():
font_family = Defs.DEFAULT_TEXT_FONT_FAMILY
for f in attrs[HTML.Attrs.STYLE][HTML.Style.FONT_FAMILY].split(","):
f = f.strip()
if f in map(lambda f: f.lower(), font.families()):
font_family = f
break
self._stack_add(tag, Fnt.FAMILY, font_family)
elif tag in (HTML.Tag.PRE, HTML.Tag.CODE):
self._stack_add(tag, Fnt.FAMILY, self.PREFORMATTED_FONT_FAMILY)
else:
self._stack_add(tag, Fnt.FAMILY)
# ---------------------------------------------------------------------------- [ FONT_SIZE ]
if HTML.Style.FONT_SIZE in attrs[HTML.Attrs.STYLE].keys():
size = Defs.FONT_SIZE
if attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE].endswith("px"):
if attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE][:-2].isdigit():
size = int(attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE][:-2])
elif attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE].endswith(r"%"):
if attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE][:-1].isdigit():
size = int((int(attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE][:-1]) * Defs.FONT_SIZE) / 100)
self._stack_add(tag, Fnt.SIZE, size)
elif tag.startswith('h') and len(tag) == 2:
self._stack_add(tag, Fnt.SIZE, Defs.HEADINGS_FONT_SIZE[tag])
else:
self._stack_add(tag, Fnt.SIZE)
# --------------------------------------------------------------------------- [ TEXT_ALIGN ]
if HTML.Style.TEXT_ALIGN in attrs[HTML.Attrs.STYLE].keys() and tag in HTML.TEXT_ALIGN_TAGS:
self._stack_add(tag, WCfg.JUSTIFY, attrs[HTML.Attrs.STYLE][HTML.Style.TEXT_ALIGN])
else:
self._stack_add(tag, WCfg.JUSTIFY)
# ---------------------------------------------------------------------- [ TEXT_DECORATION ]
if HTML.Style.TEXT_DECORATION in attrs[HTML.Attrs.STYLE].keys():
if tag == HTML.Tag.STRONG:
self._stack_add(tag, Fnt.UNDERLINE, False)
self._stack_add(tag, Fnt.OVERSTRIKE, False)
elif HTML.StyleTextDecoration.UNDERLINE in attrs[HTML.Attrs.STYLE][HTML.Style.TEXT_DECORATION]:
self._stack_add(tag, Fnt.UNDERLINE, True)
self._stack_add(tag, Fnt.OVERSTRIKE, False)
elif HTML.StyleTextDecoration.LINE_THROUGH in attrs[HTML.Attrs.STYLE][HTML.Style.TEXT_DECORATION]:
self._stack_add(tag, Fnt.UNDERLINE, False)
self._stack_add(tag, Fnt.OVERSTRIKE, True)
else:
self._stack_add(tag, Fnt.UNDERLINE)
self._stack_add(tag, Fnt.OVERSTRIKE)
else:
if tag == HTML.Tag.A and attrs[HTML.Attrs.HREF]:
self._stack_add(tag, Fnt.UNDERLINE, True)
self._stack_add(tag, Fnt.OVERSTRIKE, False)
elif tag == HTML.Tag.U:
self._stack_add(tag, Fnt.UNDERLINE, True)
self._stack_add(tag, Fnt.OVERSTRIKE, False)
else:
self._stack_add(tag, Fnt.UNDERLINE)
self._stack_add(tag, Fnt.OVERSTRIKE)
def handle_starttag(self, tag, attrs):
# ------------------------------------------------------------------------------------------
tag = tag.lower()
attrs = self._parse_attrs(attrs)
if tag in HTML.STYLE_TAGS:
# ---------------------------------------------------------------------- [ STYLED_TAGS ]
self._parse_styles(tag, attrs)
if tag == HTML.Tag.B or tag == HTML.Tag.STRONG or tag in HTML.HEADING_TAGS:
self._stack_add(tag, Fnt.WEIGHT, "bold")
elif tag == HTML.Tag.I or tag == HTML.Tag.EM:
self._stack_add(tag, Fnt.SLANT, "italic")
elif tag == HTML.Tag.A:
self._stack_add(tag, Bind.LINK, attrs[HTML.Attrs.HREF])
elif tag == HTML.Tag.OL:
# ---------------------------------------------------------------- [ ORDERED_LISTS ]
if attrs[HTML.Attrs.TYPE] and attrs[HTML.Attrs.TYPE] in HTML.TypeOrderedList.__dict__.values():
list_type = attrs[HTML.Attrs.TYPE]
else:
list_type = HTML.TypeOrderedList._1
self.list_tags.append(ListTag(ordered=True, list_type=list_type))
tabs = []
for i in range(len(self.list_tags)):
offset = 30 * (i + 1)
tabs += [offset, tk.RIGHT, offset + 5, tk.LEFT]
self._stack_add(tag, WCfg.TABS, tabs)
elif tag == HTML.Tag.UL:
# -------------------------------------------------------------- [ UNORDERED_LISTS ]
self.list_tags.append(ListTag(ordered=False))
tabs = []
for i in range(len(self.list_tags)):
offset = 30 * (i + 1)
tabs += [offset, tk.RIGHT, offset + 5, tk.LEFT]
self._stack_add(tag, WCfg.TABS, tabs)
elif tag == HTML.Tag.LI:
# ------------------------------------------------------------------ [ LISTS_LINES ]
level = len(self.list_tags)
if level:
self.list_tags[-1].add()
if self.strip:
self._insert_new_line()
line_index = self.list_tags[-1].line_index()
if self.list_tags[-1].ordered:
line_index = "\t" + "\t\t" * (level - 1) + line_index + ".\t"
else:
line_index = "\t" + "\t\t" * (level - 1) + line_index + "\t"
self._stack_add(tag, Fnt.UNDERLINE, False)
self._stack_add(tag, Fnt.OVERSTRIKE, False)
self._w_tags_add()
self._w.insert(tk.INSERT, line_index)
self._stack_pop(tag, Fnt.UNDERLINE)
self._stack_pop(tag, Fnt.OVERSTRIKE)
elif tag == HTML.Tag.IMG and attrs[HTML.Attrs.SRC]:
# -------------------------------------------------------------------- [ UNSTYLED_TAGS ]
image = None
print(attrs[HTML.Attrs.SRC], self.cached_images)
if attrs[HTML.Attrs.SRC].startswith(("https://", "ftp://", "http://")):
if attrs[HTML.Attrs.SRC] in self.cached_images.keys():
image = deepcopy(self.cached_images[attrs[HTML.Attrs.SRC]])
else:
try:
image = Image.open(BytesIO(requests.get(attrs[HTML.Attrs.SRC]).content))
self.cached_images[attrs[HTML.Attrs.SRC]] = deepcopy(image)
except:
pass
if attrs[HTML.Attrs.SRC] in self.cached_images.keys():
image = deepcopy(self.cached_images[attrs[HTML.Attrs.SRC]])
elif os.path.exists(attrs[HTML.Attrs.SRC]):
image = Image.open(attrs[HTML.Attrs.SRC])
self.cached_images[attrs[HTML.Attrs.SRC]] = deepcopy(image)
if image:
width = image.size[0]
height = image.size[1]
resize = False
if str(attrs[HTML.Attrs.WIDTH]).isdigit():
width = int(attrs[HTML.Attrs.WIDTH])
resize = True
if str(attrs[HTML.Attrs.HEIGHT]).isdigit():
height = int(attrs[HTML.Attrs.HEIGHT])
resize = True
if resize:
image = image.resize((width, height), Image.ANTIALIAS)
self.images.append(ImageTk.PhotoImage(image))
self._w.image_create(tk.INSERT, image=self.images[-1])
if self.strip:
# ------------------------------------------------------------------------ [ NEW_LINES ]
if tag == HTML.Tag.BR:
self._insert_new_line()
else:
self.html_tags.append(tag)
if tag in HTML.NEW_LINE_TAGS and self.strip and self._w.index("end-1c") != "1.0":
if tag in (HTML.Tag.DIV,):
self._insert_new_line()
elif tag in (HTML.Tag.UL, HTML.Tag.OL):
if len(self.list_tags) == 1:
self._insert_new_line(double=True)
else:
self._insert_new_line(double=False)
else:
self._insert_new_line(double=True)
self._w_tags_add()
def handle_charref(self, data):
# ------------------------------------------------------------------------------------------
try:
char = chr(int(data))
self._w.insert(tk.INSERT, char)
except:
pass
def _insert_new_line(self, double=False):
# ------------------------------------------------------------------------------------------
self._remove_last_space()
if self._w.get("end-3c", "end-1c") == "\n\n":
pass
elif self._w.get("end-2c", "end-1c") == "\n":
if double:
self._w.insert(tk.INSERT, "\n")
else:
if double:
self._w.insert(tk.INSERT, "\n\n")
else:
self._w.insert(tk.INSERT, "\n")
def _text_rstrip(self):
# ------------------------------------------------------------------------------------------
for _ in range(3):
if self._w.get("end-2c", "end-1c") in (" ", "\n"):
self._w.delete("end-2c", "end-1c")
def _remove_last_space(self):
# ------------------------------------------------------------------------------------------
if self._w.get("end-2c", "end-1c") == " ":
self._w.delete("end-2c", "end-1c")
def _remove_multi_spaces(self, data):
# ------------------------------------------------------------------------------------------
data = data.replace(" ", " ")
if " " in data:
data = self._remove_multi_spaces(data)
return data
def handle_data(self, data):
# ------------------------------------------------------------------------------------------
if self.strip:
if len(self.html_tags) and self.html_tags[-1] in (HTML.Tag.PRE, HTML.Tag.CODE):
pass
elif not data.strip():
data = ""
else:
# left strip
if self._w.index("end-1c").endswith(".0"):
data = data.lstrip()
elif self._w.get("end-2c", "end-1c") == " ":
data = data.lstrip()
data = data.replace("\n", " ").replace("\t", " ")
data = data + " "
data = self._remove_multi_spaces(data)
if len(self.html_tags):
level = len(self.list_tags)
if self.html_tags[-1] in (HTML.Tag.UL, HTML.Tag.OL):
self._w.insert(tk.INSERT, "\t" * 2 * level)
self._w.insert(tk.INSERT, data)
def handle_endtag(self, tag):
# ------------------------------------------------------------------------------------------
tag = tag.lower()
try:
index = len(self.html_tags) - self.html_tags[::-1].index(tag) - 1
self.html_tags.pop(index)
except:
pass
if tag in HTML.STYLE_TAGS:
self._stack_pop(tag, WCfg.FOREGROUND)
self._stack_pop(tag, WCfg.BACKGROUND)
self._stack_pop(tag, WCfg.JUSTIFY)
self._stack_pop(tag, Fnt.FAMILY)
self._stack_pop(tag, Fnt.SIZE)
self._stack_pop(tag, Fnt.UNDERLINE)
self._stack_pop(tag, Fnt.OVERSTRIKE)
if tag == HTML.Tag.B or tag == HTML.Tag.STRONG or tag in HTML.HEADING_TAGS:
self._stack_pop(tag, Fnt.WEIGHT)
elif tag == HTML.Tag.I or tag == HTML.Tag.EM:
self._stack_pop(tag, Fnt.SLANT)
elif tag == HTML.Tag.A:
self._stack_pop(tag, Bind.LINK)
elif tag == HTML.Tag.OL or tag == HTML.Tag.UL:
if len(self.list_tags):
self.list_tags = self.list_tags[:-1]
self._stack_pop(tag, WCfg.TABS)
if tag in HTML.NEW_LINE_TAGS and self.strip:
self._insert_new_line()
self._w_tags_add()
if tag in HTML.NEW_LINE_TAGS and self.strip:
if tag in (HTML.Tag.DIV, HTML.Tag.UL, HTML.Tag.OL):
if not len(self.list_tags):
self._insert_new_line(double=True)
else:
self._insert_new_line(double=True)
def _w_tags_apply_all(self):
# ------------------------------------------------------------------------------------------
# update indexes
if self.strip:
self._text_rstrip()
end_index = tk.END
for key, tag in reversed(tuple(self._w_tags.items())):
tag[WTag.START_INDEX] = key
tag[WTag.END_INDEX] = end_index
end_index = key
# add tags
self.hlink_slots = []
for key, tag in self._w_tags.items():
self._w.tag_add(key, tag[WTag.START_INDEX], tag[WTag.END_INDEX])
self._w.tag_config(key, font=font.Font(**tag[Fnt.KEY]), **tag[WCfg.KEY])
if tag[Bind.KEY][Bind.LINK]:
self.hlink_slots.append(HLinkSlot(self._w, key, tag[Bind.KEY][Bind.LINK]))
self._w.tag_bind(key, "<Button-1>", self.hlink_slots[-1].call)
self._w.tag_bind(key, "<Leave>", self.hlink_slots[-1].leave)
self._w.tag_bind(key, "<Enter>", self.hlink_slots[-1].enter)
def w_set_html(self, w, html, strip):
# ------------------------------------------------------------------------------------------
self._w = w
self.stack = deepcopy(DEFAULT_STACK)
self.stack[WCfg.KEY][WCfg.BACKGROUND].append(("__DEFAULT__", self._w.cget("background")))
self.stack[Fnt.KEY][Fnt.FAMILY].append(("__DEFAULT__", self.DEFAULT_TEXT_FONT_FAMILY))
self._w_tags = OrderedDict()
self.html_tags = []
self.images = []
self.list_tags = []
self.strip = strip
self._w_tags_add()
self.feed(html)
self._w_tags_apply_all()
del self._w

View File

@ -10,5 +10,3 @@ pyautogui
requests
beautifulsoup4
whatsmyip
pypng
tkhtmlview

View File

@ -1,6 +1,6 @@
cd temp\test
Remove-Item venv -Recurse
& conda create --prefix venv python=3.7 -y
& conda create --prefix venv python=3.7.3 -y
conda activate ./venv
cd ../../dist
pip install ((dir).Name | grep whl)