Skip to content

Instantly share code, notes, and snippets.

@tamamu
Created June 17, 2020 09:55
Show Gist options
  • Save tamamu/00b8ac7dc3644f46c734101d61256cda to your computer and use it in GitHub Desktop.
Save tamamu/00b8ac7dc3644f46c734101d61256cda to your computer and use it in GitHub Desktop.
Super Simplified Web Browser
import tkinter as tk
import urllib.request
import io
from PIL import Image, ImageTk
from urllib.parse import urljoin
from html.parser import HTMLParser
from tkx import VerticalScrolledFrame
class MyHTMLParser(HTMLParser):
def __init__(self, parent):
super().__init__()
self.cur_tag = None
self.cur_attrs = None
self.cur_url = ""
self.parent = parent
def handle_starttag(self, tag, attrs):
self.cur_tag = tag
self.cur_attrs = {attr[0]:attr[1] for attr in attrs}
if self.cur_tag == 'img':
src = self.cur_attrs['src']
if src != "http":
src = urljoin(self.cur_url, src)
with urllib.request.urlopen(src) as f:
pilimg = Image.open(io.BytesIO(f.read()))
img = ImageTk.PhotoImage(pilimg)
self.parent.add_image(img)
return
def handle_data(self, data):
if self.cur_tag == "a":
href = self.cur_attrs['href']
if href != "http":
href = urljoin(self.cur_url, href)
self.parent.add_link(data, href)
return
if self.cur_tag not in ['script', 'style'] and data.strip() != "":
self.parent.add_text(data)
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.parser = MyHTMLParser(self)
self.master = master
self.cache = []
self.pack(expand=1, fill=tk.BOTH, anchor=tk.NW)
self.create_widgets()
def create_widgets(self):
# URL入力バー
self.url_bar = tk.Entry(self)
self.url_bar.grid(column = 0, row = 0, sticky = 'nsew')
self.url_bar.insert(tk.END, 'https://www.google.com/')
# 移動ボタン
self.url_button = tk.Button(self, text="GO", command=self.enter_url, fg='RED')
self.url_button.grid(column = 1, row = 0, sticky = 'nsew')
self.columnconfigure(0, weight=1)
# ページフレーム
self.page = VerticalScrolledFrame(self, bg='WHITE')
self.page.grid(column = 0, row = 1, columnspan= 2, sticky = 'nsew')
self.rowconfigure(1, weight=1)
def reset_page(self):
for widget in self.page.interior.winfo_children():
widget.pack_forget()
def enter_url(self):
#ページをリセット
self.reset_page()
# URLを取得
url = self.url_bar.get()
self.parser.cur_url = url
# GETリクエスト
with urllib.request.urlopen(url) as f:
data = f.read().decode('utf-8')
self.parser.feed(data)
def add_text(self, data):
x1 = tk.Label(self.page.interior, text=data, bg='WHITE')
x1.pack()
def add_link(self, data, href):
x1 = tk.Button(self.page.interior, text=data, bg='WHITE',
command=lambda:(
self.url_bar.delete(0, tk.END),
self.url_bar.insert(tk.END, href),
self.enter_url(),
)
)
x1.pack()
def add_image(self, img):
self.cache.append(img)
x1 = tk.Label(self.page.interior, image=img)
x1.pack()
root = tk.Tk()
root.geometry('800x600')
app = Application(master=root)
app.mainloop()
from tkinter import *
class VerticalScrolledFrame(Frame):
"""A pure Tkinter scrollable frame that actually works!
* Use the 'interior' attribute to place widgets inside the scrollable frame
* Construct and pack/place/grid normally
* This frame only allows vertical scrolling
"""
def __init__(self, parent, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
# スクロールバーの作成
vscrollbar = Scrollbar(self, orient=VERTICAL)
vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
canvas = Canvas(self, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set, bg='WHITE')
canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
vscrollbar.config(command=canvas.yview)
# ビューをリセット
canvas.xview_moveto(0)
canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = Frame(canvas, bg='WHITE')
interior_id = canvas.create_window(0, 0, window=interior,
anchor=NW)
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the canvas's width to fit the inner frame
canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the inner frame's width to fill the canvas
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment