Merge pull request #4 from sloumdrone/refactor-classes

Refactor classes
This commit is contained in:
Brian 2018-10-27 15:35:02 -07:00 committed by GitHub
commit e581515e3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 504 additions and 333 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

13
burrow.py Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
################################################
## Burrow, a gopher client/browser
## - - - - - - - - - - - - - - - - - - - - - - -
## Version 0.2.1
################################################
from gui import GUI
if __name__ == '__main__':
app = GUI()
app.root.mainloop()

97
conn.py
View File

@ -1,97 +0,0 @@
import socket
import sys
import re
class Tunnel:
def __init__(self):
self.raw_request = None
self.text_output = None
self.types = {
'0': '(TXT)',
'1': '(MENU)',
'2': None,
'3': 'Error code',
'4': None,
'5': None,
'6': None,
'7': '(INTER)',
'8': 'Telnet',
'9': '(BIN)',
'+': None,
'g': '(GIF)',
'I': '(IMG)',
't': None,
'h': '(HTML)',
'i': '(INFO)',
's': '(SOUND)'
}
def make_connection(self, resource, host, itemtype, port=70):
endline = '\r\n'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(20.0)
try:
port = int(port)
except:
port = 70
s.connect((host, port))
s.sendall((resource + '\r\n').encode('utf-8'))
r = s.makefile(mode = 'r')
raw_data = r.read()
try:
data = raw_data.decode('utf-8')
except AttributeError:
data = raw_data
self.raw_request = data
if itemtype[1] == '1':
#handle menus
self.text_output = self.gopher_to_text()
elif itemtype[1] == '0':
#handle text files
self.text_output = [self.raw_request]
self.text_output.insert(0,itemtype[1])
s.close()
def gopher_to_text(self):
message = self.raw_request.split('\n')
message = [x.split('\t') for x in message]
message = [{'type': x[0][0], 'description': x[0][1:], 'resource': x[1], 'host': x[2], 'port': x[3]} for x in message if len(x) >= 4]
return message
def parse_url(self, url):
regex = r'^(?P<protocol>(?:gopher:\/\/)?)?(?P<host>[\w\.\d]+)(?P<port>(?::\d+)?)?(?P<type>(?:\/\d)?)?(?P<resource>(?:\/[\w\/\d\-?.]*)?)?$'
match = re.match(regex, url)
protocol = match.group('protocol')
itemtype = match.group('type')
host = match.group('host')
port = match.group('port')
resource = match.group('resource')
if protocol != 'gopher://' and protocol:
return False
if itemtype and not self.types[itemtype[1]]:
return False
elif not itemtype:
itemtype = '/1'
if not host:
return False
if not resource:
resource = '/'
if port:
port = port[1:]
self.make_connection(resource, host, itemtype, port)
if __name__ == '__main__':
inp = sys.argv[1:]
if len(inp) >= 2:
test = Tunnel()
test.make_connection(inp[1],inp[0],'70')
else:
print('Incorrect request')

29
connect.py Normal file
View File

@ -0,0 +1,29 @@
import socket
class connect:
def __init__(self):
self.raw_response = None
self.filetype = None
def request(self, resource, host, itemtype, port=70):
#connects to server and returns list with response type and body
socket_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket_conn.settimeout(20.0)
socket_conn.connect((host, port))
socket_conn.sendall((resource + '\r\n').encode('utf-8'))
if itemtype in ['I','p','g']:
response = socket_conn.makefile(mode = 'rb', errors = 'ignore')
else:
response = socket_conn.makefile(mode = 'r', errors = 'ignore')
try:
self.raw_response = response.read()
self.filetype = itemtype
except UnicodeDecodeError:
self.raw_response = '3Error decoding server response\tfalse\tnull.host\t1'
self.filetype = '3'
socket_conn.close()
return {'type': self.filetype, 'body': self.raw_response}

236
digger.py
View File

@ -1,236 +0,0 @@
#!/usr/bin/env python3
import tkinter as tk
from conn import Tunnel as go
import time
import sys
class GUI:
def __init__(self):
self.history = []
self.history_location = -1
self.message_bar_content = ''
#colors
self.FG = 'black'
self.BG = 'white'
self.LINK = 'blue'
self.ACTIVELINK = 'red'
self.HLB = '#e37800'
self.HLF = 'white'
self.STATUS = 'silver'
#create and configure root window
self.root = tk.Tk(className='Digger')
self.root.title('Digger')
self.root.geometry("1200x800")
self.add_assets()
#main frame objects
self.top_bar = tk.Frame(self.root, padx=10, height=70, relief=tk.FLAT, bd=2)
self.body = tk.Frame(self.root, relief=tk.RIDGE, bd=2)
self.status_bar = tk.Frame(self.root, height="20", bg=self.STATUS, takefocus=0)
#top bar objects
self.btn_back = tk.Button(self.top_bar, image=self.img_back, bd=0, highlightthickness=0, takefocus=1)
self.btn_forward = tk.Button(self.top_bar,image=self.img_forward, bd=0, highlightthickness=0)
self.btn_refresh = tk.Button(self.top_bar,image=self.img_refresh, bd=0, highlightthickness=0)
self.btn_home = tk.Button(self.top_bar, image=self.img_home, bd=0, highlightthickness=0)
self.entry_url = tk.Entry(self.top_bar, selectbackground=self.HLB, selectforeground=self.HLF, highlightcolor=self.HLB)
self.btn_menu = tk.Button(self.top_bar, image=self.img_menu, bd=0, highlightthickness=0)
#body objects
self.site_display = tk.Text(self.body, bg=self.BG, foreground=self.FG, padx=20, pady=20, wrap=tk.WORD, state=tk.DISABLED, spacing2=5, spacing1=5)
self.site_display.tag_configure('linkcolor', foreground=self.LINK, spacing1=5)
self.site_display.tag_configure('type_tag', background=self.FG, foreground=self.BG, spacing2=0, spacing1=0)
#status bar objects
self.status_info = tk.Label(self.status_bar, textvariable=self.message_bar_content, bg=self.STATUS, takefocus=0)
self.pack_geometry()
self.add_status_titles()
self.add_event_listeners()
#---------------------------------------------------
def add_event_listeners(self):
buttons = [
self.btn_back,
self.btn_forward,
self.btn_refresh,
self.btn_home,
self.btn_menu
]
for x in buttons:
x.bind('<Enter>', self.update_status)
x.bind('<Leave>', self.clear_status)
self.entry_url.bind('<Return>', self.execute_address)
self.btn_back.bind('<Button-1>', self.go_back)
self.btn_forward.bind('<Button-1>', self.go_forward)
def pack_geometry(self):
self.top_bar.pack(expand=False,fill=tk.BOTH,side=tk.TOP,anchor=tk.NW)
self.top_bar.pack_propagate(False)
self.body.pack(expand=True,fill=tk.BOTH,side=tk.TOP)
self.status_bar.pack(expand=False,fill=tk.X,side=tk.TOP,anchor=tk.SW)
self.btn_back.pack(side=tk.LEFT, padx=(0,20))
self.btn_forward.pack(side=tk.LEFT, padx=(0,20))
self.btn_refresh.pack(side=tk.LEFT, padx=(0,20))
self.btn_home.pack(side=tk.LEFT, padx=(0,20))
self.entry_url.pack(side=tk.LEFT, fill=tk.X, expand=True, ipady=10, ipadx=10)
self.btn_menu.pack(side=tk.LEFT, padx=(10,0))
self.site_display.pack(expand=True, side=tk.TOP, fill=tk.BOTH)
self.status_info.pack(side=tk.LEFT)
def add_status_titles(self):
self.btn_back.pop_title = 'Back'
self.btn_forward.pop_title = 'Forward'
self.btn_refresh.pop_title = 'Refresh'
self.btn_home.pop_title = 'Home'
self.btn_menu.pop_title = 'Menu'
def add_assets(self):
self.img_back = tk.PhotoImage(file='./btn_back.png')
self.img_forward = tk.PhotoImage(file='./btn_forward.png')
self.img_refresh = tk.PhotoImage(file='./btn_refresh.png')
self.img_menu = tk.PhotoImage(file='./btn_menu.png')
self.img_home = tk.PhotoImage(file='./btn_home.png')
self.img_menu = tk.PhotoImage(file='./btn_menu.png')
self.message_bar_content = tk.StringVar()
self.message_bar_content.set('Ready.')
def execute_address(self, event, btn_url=False):
if btn_url and btn_url != -1:
url = btn_url
elif btn_url and btn_url == -1:
return
else:
url = self.entry_url.get()
self.history = self.history[:self.history_location+1]
self.history.append(url)
self.history_location = len(self.history) - 1
self.site_display.focus_set()
request = go()
request.parse_url(url)
self.send_to_screen(request.text_output)
def gotolink(self, event, href, tag_name):
res = event.widget
res.tag_config(tag_name, background=self.ACTIVELINK) # change tag text style
res.update_idletasks() # make sure change is visible
time.sleep(.5) # optional delay to show changed text
self.entry_url.delete(0,tk.END)
self.entry_url.insert(tk.END,href)
self.execute_address(0) #the zero is meaningless, but the function expects a param
res.tag_config(tag_name, background=self.BG) # restore tag text style
res.update_idletasks()
def hoverlink(self, event, href, tag_name):
self.update_status(event, href)
e = event.widget
e.tag_config(tag_name, underline=1)
e.update_idletasks()
def send_to_screen(self, data):
link_count = 0
self.site_display.config(state=tk.NORMAL)
self.site_display.delete(1.0, tk.END)
types = {
'0': '( TEXT )',
'1': '( MENU )',
'2': None,
'3': 'Error code',
'4': None,
'5': None,
'6': None,
'7': '( INTR )',
'8': 'Telnet',
'9': '( BIN )',
'+': None,
'g': '( GIF )',
'I': '( IMG )',
't': None,
'h': '( HTML )',
'i': '( INFO )',
's': '( SOUND)'
}
if data[0] == '0':
self.site_display.insert(tk.END, data[1])
elif data[0] == '1':
for x in data[1:]:
if x['type'] == 'i':
self.site_display.insert(tk.END,' \t\t{}\n'.format(x['description']))
else:
# adapted from:
# https://stackoverflow.com/questions/27760561/tkinter-and-hyperlinks
if x['port'] and x['port'][0] != ':':
x['port'] = ':{}'.format(x['port'])
link = 'gopher://{}{}/{}{}'.format(x['host'], x['port'], x['type'], x['resource'])
tag_name = 'link{}'.format(link_count)
callback = (lambda event, href=link, tag_name=tag_name: self.gotolink(event, href, tag_name))
hover = (lambda event, href=link, tag_name=tag_name: self.hoverlink(event, href, tag_name))
clear = (lambda event, tag_name=tag_name: self.clear_status(event, tag_name))
self.site_display.tag_bind(tag_name, "<Button-1>", callback)
self.site_display.tag_bind(tag_name, "<Enter>", hover)
self.site_display.tag_bind(tag_name, '<Leave>', clear)
self.site_display.insert(tk.END, types[x['type']], ('type_tag',))
self.site_display.insert(tk.END,'\t\t')
self.site_display.insert(tk.END, x['description'], (tag_name,'linkcolor'))
self.site_display.insert(tk.END, '\n')
link_count += 1
self.site_display.config(state=tk.DISABLED)
def update_status(self, event, href=False):
if href:
self.message_bar_content.set(href)
else:
self.message_bar_content.set(event.widget.pop_title)
def clear_status(self, event, tag_name=False):
if tag_name:
e = event.widget
e.tag_config(tag_name, underline=0)
e.update_idletasks()
self.message_bar_content.set('')
def go_back(self, event):
if len(self.history) > 1 and self.history_location > 0:
self.history_location -= 1
href = self.history[self.history_location]
self.entry_url.delete(0, tk.END)
self.entry_url.insert(tk.END, href)
else:
href = -1
self.execute_address(False, href)
def go_forward(self, event):
if len(self.history) > 1 and self.history_location < len(self.history) - 1:
self.history_location += 1
href = self.history[self.history_location]
self.entry_url.delete(0,tk.END)
self.entry_url.insert(tk.END, href)
else:
href = -1
self.execute_address(False, href)
if __name__ == '__main__':
app = GUI()
app.root.mainloop()

1
go.config.json Normal file
View File

@ -0,0 +1 @@
{"favorites": [], "last_viewed": "gopher://gopher.floodgap.com:70/1/"}

389
gui.py Normal file
View File

@ -0,0 +1,389 @@
#!/usr/bin/env python3
import tkinter as tk
from connect import connect as conn
from parser import parser
import time
import sys
import json
import os.path
from io import BytesIO
from PIL import Image, ImageTk
class GUI:
def __init__(self):
self.history = []
self.history_location = -1
self.message_bar_content = ''
self.config = None
self.read_config()
self.conn = conn()
self.parser = parser()
#colors
self.FG = '#E0E2E4'
self.BG = '#2F393C'
self.LINK = '#E8E2B7'
self.ACTIVELINK = '#678CB1'
self.HLB = '#804000'
self.HLF = '#E0E2E4'
self.STATUS_BG = '#293134'
self.STATUS_FG = '#FFCD22'
self.ERROR = '#E8E2B7'
self.BAR_BG = '#293134'
self.BAR_FG = '#2F393C'
self.BAR_HLB = '#804000'
self.BAR_HLF = '#E0E2E4'
self.BAR_SLOT = '#E0E2E4'
self.SCROLL = '#434A57'
self.TYPES = '#A082BD'
#create and configure root window
self.root = tk.Tk(className='Digger')
self.root.title('Digger')
self.root.geometry("1200x800")
self.add_assets()
#main frame objects
self.top_bar = tk.Frame(self.root, padx=10, height=50, relief=tk.FLAT, bd=2, bg=self.BAR_BG)
self.body = tk.Frame(self.root, relief=tk.FLAT, bd=0, bg=self.BG)
self.status_bar = tk.Frame(self.root, height="20", relief=tk.FLAT, bg=self.STATUS_BG, takefocus=0)
#top bar objects
self.btn_back = tk.Button(self.top_bar, image=self.img_back, bd=0, highlightthickness=0, takefocus=1, bg=self.BAR_BG)
self.btn_forward = tk.Button(self.top_bar,image=self.img_forward, bd=0, highlightthickness=0, bg=self.BAR_BG)
self.btn_favorite = tk.Button(self.top_bar,image=self.img_favorite, bd=0, highlightthickness=0, bg=self.BAR_BG)
self.btn_home = tk.Button(self.top_bar, image=self.img_home, bd=0, highlightthickness=0, bg=self.BAR_BG)
self.entry_url = tk.Entry(self.top_bar, selectbackground=self.HLB, selectforeground=self.HLF, highlightcolor=self.FG, highlightbackground=self.BAR_BG, fg=self.BAR_FG, bg=self.BAR_SLOT)
self.btn_menu = tk.Button(self.top_bar, image=self.img_menu, bd=0, highlightthickness=0, bg=self.BAR_BG)
#body objects
self.scroll_bar = tk.Scrollbar(self.body, bg=self.BAR_BG, bd=0, highlightthickness=0, troughcolor=self.BG, activebackground=self.SCROLL, activerelief=tk.RAISED)
self.site_display = tk.Text(self.body, bg=self.BG, foreground=self.FG, padx=20, pady=20, wrap=tk.WORD, state=tk.DISABLED, spacing2=2, spacing1=2, spacing3=2, yscrollcommand=self.scroll_bar.set, highlightcolor=self.BG, highlightbackground=self.BAR_BG, relief=tk.FLAT)
self.scroll_bar.config(command=self.site_display.yview, width=20, relief=tk.RIDGE)
self.site_display.tag_configure('linkcolor', foreground=self.LINK, spacing1=5, spacing2=5, spacing3=5)
self.site_display.tag_configure('type_tag', background=self.BG, foreground=self.TYPES, spacing2=1, spacing1=1, spacing3=1)
self.site_display.tag_configure('error_text', foreground=self.ERROR, spacing1=5, spacing2=5, spacing3=5)
#status bar objects
self.status_info = tk.Label(self.status_bar, textvariable=self.message_bar_content, bg=self.STATUS_BG, takefocus=0, fg=self.ACTIVELINK)
self.pack_geometry()
self.add_status_titles()
self.add_event_listeners()
#load the home screen
self.load_home_screen(1)
#-----------Start GUI configuration-----------------------
def add_event_listeners(self):
buttons = [
self.btn_back,
self.btn_forward,
self.btn_favorite,
self.btn_home,
self.btn_menu
]
for x in buttons:
x.bind('<Enter>', self.update_status)
x.bind('<Leave>', self.clear_status)
x.config(activebackground=self.BG)
self.entry_url.bind('<Return>', self.execute_address)
self.btn_back.bind('<Button-1>', self.go_back)
self.btn_forward.bind('<Button-1>', self.go_forward)
self.btn_home.bind('<Button-1>', self.load_home_screen)
self.site_display.bind("<Up>", lambda event: self.site_display.yview_scroll(-1, 'units'))
self.site_display.bind("<Down>", lambda event: self.site_display.yview_scroll(1, 'units'))
self.site_display.bind("<Button-1>", lambda event: self.site_display.focus_set())
self.entry_url.bind("<Button-1>", lambda event: self.entry_url.focus_set())
self.root.protocol('WM_DELETE_WINDOW', self.close_window)
def pack_geometry(self):
self.top_bar.pack(expand=False,fill=tk.BOTH,side=tk.TOP,anchor=tk.NW)
self.top_bar.pack_propagate(False)
self.body.pack(expand=True,fill=tk.BOTH,side=tk.TOP)
self.status_bar.pack(expand=False,fill=tk.X,side=tk.TOP,anchor=tk.SW)
self.btn_back.pack(side=tk.LEFT, padx=(0,20))
self.btn_forward.pack(side=tk.LEFT, padx=(0,20))
self.btn_home.pack(side=tk.LEFT, padx=(0,20))
self.entry_url.pack(side=tk.LEFT, fill=tk.X, expand=True, ipady=5, ipadx=10)
self.btn_favorite.pack(side=tk.LEFT, padx=(10,10))
self.btn_menu.pack(side=tk.LEFT)
self.scroll_bar.pack(side=tk.RIGHT,fill=tk.Y)
self.site_display.pack(expand=True, side=tk.TOP, fill=tk.BOTH)
self.status_info.pack(side=tk.LEFT)
def add_status_titles(self):
self.btn_back.pop_title = 'Back'
self.btn_forward.pop_title = 'Forward'
self.btn_favorite.pop_title = 'Favorite'
self.btn_home.pop_title = 'Home'
self.btn_menu.pop_title = 'Menu'
def add_assets(self):
self.img_back = tk.PhotoImage(file='./images/back.png')
self.img_forward = tk.PhotoImage(file='./images/forward.png')
self.img_favorite = tk.PhotoImage(file='./images/favorite.png')
self.img_home = tk.PhotoImage(file='./images/home.png')
self.img_menu = tk.PhotoImage(file='./images/settings.png')
self.message_bar_content = tk.StringVar()
self.message_bar_content.set('Ready.')
# ------------Start navigation methods----------------------------
def execute_address(self, event=False, btn_url=False, history=True):
url = btn_url if btn_url else self.entry_url.get()
if url == 'home':
adjust_history = None if btn_url else 1
self.load_home_screen(adjust_history)
return True
parsed_url = self.parser.parse_url(url)
if not parsed_url:
# To do: build errors class to handle displaying errors
# return errors.url_error
return False
if parsed_url['type'] == '7':
self.send_to_screen(parsed_url, parsed_url['type'])
response = self.conn.request(self.parser.resource, self.parser.host, self.parser.filetype, self.parser.port)
if not response:
# To do: build errors class to handle displaying errors
# return errors.connection_error_NUMBER
return False
if history:
self.history = self.history[:self.history_location+1]
self.history.append(url)
self.history_location = len(self.history) - 1
# Get the data to the screen
self.site_display.focus_set()
self.config["last_viewed"] = url
self.send_to_screen(self.conn.raw_response, self.conn.filetype)
return True
def add_to_history(self, url):
self.history = self.history[:self.history_location+1]
self.history.append(url)
self.history_location = len(self.history) - 1
def gotolink(self, event, href, tag_name):
element = event.widget
element.tag_config(tag_name, background=self.ACTIVELINK)
element.update_idletasks() # make sure change is visible
time.sleep(.5) # optional delay to show changed text
self.entry_url.delete(0,tk.END)
self.entry_url.insert(tk.END,href)
success = self.execute_address()
element.tag_config(tag_name, background=self.BG) # restore tag text style
element.update_idletasks()
def load_home_screen(self,event=None):
with open('./home.gopher','r') as f:
data = f.read()
self.entry_url.delete(0, tk.END)
self.entry_url.insert(tk.END, 'home')
if event is not None:
self.add_to_history('home')
self.send_to_screen(data, '1')
def go_back(self, event):
if len(self.history) <= 1 or self.history_location <= 0:
return False
self.history_location -= 1
href = self.history[self.history_location]
self.populate_url_bar(href)
self.execute_address(False, href, False)
def go_forward(self, event):
if len(self.history) <= 1 or self.history_location >= len(self.history) - 1:
return False
self.history_location += 1
href = self.history[self.history_location]
self.populate_url_bar(href)
self.execute_address(False, href, False)
#-------------Start view methods----------------
def load_favorites(self):
header = 'i#############\tfalse\tnull.host\t1\r\ni manually edit in go.config.json\tfalse\tnull.host\t1\r\n or add using the favorites button\tfalse\tnull.host\t1\r\ni\tfalse\tnull.host\t1\r\n'
#soon add code to load in favorites here
self.send_to_screen(data=header, clear=False)
def show_menu(self, data, clear=True):
if not data:
#error handling will go here
return False
types = {
'0': '( TXT )',
'1': '( MNU )',
'3': '( ERR )',
'7': '( INT )',
'9': '( BIN )',
'g': '( GIF )',
'I': '( IMG )',
'h': '( HTM )',
'i': '( INF )',
's': '( SND )',
'p': '( PNG )'
}
self.site_display.config(state=tk.NORMAL)
if clear:
self.site_display.delete(1.0, tk.END)
link_count = 0
for x in data[1:]:
if x['type'] == 'i':
self.site_display.insert(tk.END,' \t\t{}\n'.format(x['description']))
elif x['type'] == '3':
self.site_display.insert(tk.END,' \t\t{}\n'.format(x['description']))
elif x['type'] in types:
# adapted from:
# https://stackoverflow.com/questions/27760561/tkinter-and-hyperlinks
if x['port'] and x['port'][0] != ':':
x['port'] = ':{}'.format(x['port'])
link = 'gopher://{}{}/{}{}'.format(x['host'], x['port'], x['type'], x['resource'])
tag_name = 'link{}'.format(link_count)
callback = (lambda event, href=link, tag_name=tag_name: self.gotolink(event, href, tag_name))
hover = (lambda event, href=link, tag_name=tag_name: self.hoverlink(event, href, tag_name))
clear = (lambda event, tag_name=tag_name: self.clear_status(event, tag_name))
self.site_display.tag_bind(tag_name, "<Button-1>", callback)
self.site_display.tag_bind(tag_name, "<Enter>", hover)
self.site_display.tag_bind(tag_name, '<Leave>', clear)
self.site_display.insert(tk.END, types[x['type']], ('type_tag',))
self.site_display.insert(tk.END,'\t\t')
self.site_display.insert(tk.END, x['description'], (tag_name,'linkcolor'))
self.site_display.insert(tk.END, '\n')
link_count += 1
self.site_display.config(state=tk.DISABLED)
return True
def show_text(self, data):
self.site_display.config(state=tk.NORMAL)
self.site_display.delete(1.0, tk.END)
self.site_display.insert(tk.END, data[:-2])
self.site_display.config(state=tk.DISABLED)
def show_image(self, data):
self.current_image = self.build_image(data)
self.site_display.config(state=tk.NORMAL)
self.site_display.delete(1.0, tk.END)
self.site_display.image_create(tk.END, image = self.current_image)
self.site_display.config(state=tk.DISABLED)
def send_to_screen(self, data, itemtype='1', clear=True):
if itemtype == '0':
self.show_text(data)
elif itemtype in ['1','3']:
data = self.parser.parse_menu(data)
self.show_menu(data, clear)
elif itemtype in ['p','I','g']:
self.show_image(data)
elif itemtype == '7':
pass
def update_status(self, event, href=False):
if href:
self.message_bar_content.set(href)
else:
self.message_bar_content.set(event.widget.pop_title)
def clear_status(self, event, tag_name=False):
if tag_name:
e = event.widget
e.tag_config(tag_name, underline=0)
self.site_display.config(cursor='xterm')
e.update_idletasks()
self.message_bar_content.set('')
def populate_url_bar(self, url):
self.entry_url.delete(0, tk.END)
self.entry_url.insert(tk.END, url)
def hoverlink(self, event, href, tag_name):
self.update_status(event, href)
e = event.widget
e.tag_config(tag_name, underline=1, foreground=self.LINK)
self.site_display.config(cursor="arrow")
e.update_idletasks()
def build_image(self, bytes_str):
stream = BytesIO(bytes_str)
pilimage = Image.open(stream)
tkimage = ImageTk.PhotoImage(pilimage)
return tkimage
#--------Start file handling methods------------
def read_config(self, url='./go.config.json'):
if not os.path.isfile(url):
self.create_config()
with open('./go.config.json', 'r') as f:
config = f.read()
config = json.loads(config)
self.config = config
def write_config(self, config, url='./go.config.json'):
with open(url, 'w') as f:
data = json.dumps(config)
f.write(data)
def create_config(self):
config = {"favorites": [],"last_viewed": None}
self.write_config(config)
def close_window(self):
self.write_config(self.config)
self.root.destroy()
if __name__ == '__main__':
app = GUI()
app.root.mainloop()

18
home.gopher Normal file
View File

@ -0,0 +1,18 @@
i false null.host 1
i██████╗ ██╗ ██╗██████╗ ██████╗ ██████╗ ██╗ ██╗ false null.host 1
i██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔═══██╗██║ ██║ false null.host 1
i██████╔╝██║ ██║██████╔╝██████╔╝██║ ██║██║ █╗ ██║ false null.host 1
i██╔══██╗██║ ██║██╔══██╗██╔══██╗██║ ██║██║███╗██║ false null.host 1
i██████╔╝╚██████╔╝██║ ██║██║ ██║╚██████╔╝╚███╔███╔╝ false null.host 1
i╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══╝╚══╝ false null.host 1
i false null.host 1
i##########CONTENT PORTALS########## false null.host 1
i false null.host 1
1Floodgap / gopher.floodgap.com 70
1Super Dimensional Fortress / sdf.org 70
i false null.host 1
i false null.host 1
i#############FAVORITES############# false null.host 1
i manually edit in favorites.json false null.host 1
i or add using the favorites button false null.host 1
i false null.host 1

BIN
images/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 979 B

BIN
images/favorite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
images/favorite2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
images/forward.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
images/forward2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
images/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
images/home2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
images/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 963 B

54
parser.py Normal file
View File

@ -0,0 +1,54 @@
import re
# Handles parsing gopher data:
# URLs, Menus
class parser:
def __init__(self):
self.host = None
self.resource = None
self.filetype = None
self.protocol = None
self.port = None
self.menu = None
def parse_url(self, url):
# Take in a URL and output a dict of the url parts
regex = r'^(?P<protocol>(?:gopher:\/\/)?)?(?P<host>[\w\.\d]+)(?P<port>(?::\d+)?)?(?P<type>(?:\/[\dgIp])?)?(?P<resource>(?:\/[\w\/\d\-?.]*)?)?$'
match = re.match(regex, url)
protocol = match.group('protocol')
itemtype = match.group('type')
host = match.group('host')
port = match.group('port')
resource = match.group('resource')
if not host:
return False
if not resource:
resource = '/'
self.filetype = itemtype[len(itemtype) - 1] if itemtype else '1'
self.protocol = protocol if protocol else 'gopher://'
self.port = int(port[1:]) if port else 70
self.host = host
self.resource = resource
return {'host': self.host, 'resource': self.resource, 'type': self.filetype, 'protocol': self.protocol, 'port': self.port}
def parse_menu(self, text):
# Take in text from connection and output a list
# w/ objects representing each menu item
message_list = text.split('\n')
message_list = [x.split('\t') for x in message_list]
message_list = [{'type': x[0][0], 'description': x[0][1:], 'resource': x[1], 'host': x[2], 'port': x[3]} for x in message_list if len(x) >= 4]
self.menu = message_list
return message_list