Skip to content

Commit

Permalink
Fatal errors close WinMain on auto_run. Fixes: #6
Browse files Browse the repository at this point in the history
A little joke was added to `WinAbout` when clicking
the TigerTamer icon too many times.
  • Loading branch information
cjwelborn committed Apr 22, 2019
1 parent 9b95748 commit 61384b0
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 95 deletions.
169 changes: 112 additions & 57 deletions lib/gui/about.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
-Christopher Welborn 01-05-2019
"""

import random
import string

from platform import platform

from .common import (
Expand Down Expand Up @@ -122,8 +125,9 @@ def __init__(
self.lbl_img.image = self.img_icon
self.lbl_img.grid(row=0, column=0, sticky=tk.NSEW)
self.lbl_img.bind('<ButtonRelease>', self.cmd_lbl_img_click)
# See self.cmd_lbl_img_click
self.img_dimmed = False
# See cmd_lbl_img_click()..
self.lbl_clicks = 0
self.appending_garbage = False

# ..Main information panel.
self.frm_tt = ttk.Frame(
Expand Down Expand Up @@ -170,88 +174,107 @@ def __init__(
self.scroll_info = ttk.Scrollbar(self.frm_info)
self.scroll_info.grid(row=0, column=1, sticky=tk.NSEW)
# ..Sys Info Text
max_info_lines = 11
max_info_cols = len(PLATFORM) + 5
sysinfo = self.get_info()
sysinfolines = sysinfo.split('\n')
self.max_info_lines = len(sysinfolines)
self.max_info_cols = len(PLATFORM) + 5
self.text_info = tk.Text(
self.frm_info,
width=max_info_cols,
height=max_info_lines,
width=self.max_info_cols,
height=self.max_info_lines,
yscrollcommand=self.scroll_info.set,
bg='#ffffff',
fg='#000000',
)
self.text_info.tag_configure('error', foreground='#FF0000')
self.scroll_info.configure(command=self.text_info.yview)
self.text_info.grid(row=0, column=0, sticky=tk.NSEW)
# Insert all information into the Text widget.
self.build_info()
self.append_info(sysinfo)
# Make text read-only.
self.text_info.configure(state=tk.DISABLED)

def append_info(self, text):
""" Append lines of information into the text_info Text() widget.
def append_garbage(self, delay=0.025, callback=None):
""" Append a bunch of garbage characters to `text_info`. """
self.appending_garbage = True
chars = ''.join((string.ascii_letters, string.punctuation))
self.clear_info()
delay = int(delay * 1000)
msgs = [
'Error',
'[Deleting configuration...]',
'ERROR',
'[Deleting projects...]',
'Deleting license...',
'ID10T ERROR',
'[Calling the boss...]',
'PEBCAK FATAL ERROR',
'You shouldn\'t mess with things you don\'t understand.',
]
maxchars = (self.max_info_lines * self.max_info_cols)
maxjunkchars = maxchars - len(''.join(msgs))
chunk = maxjunkchars // len(msgs)
while msgs:
for i in range(chunk):
self.append_info(random.choice(chars))
self.after(delay)
self.update_idletasks()
for c in msgs.pop(0):
self.append_info(c)
self.after(delay)
self.update_idletasks()
if callback is None:
return
return callback()

def append_info(self, text, tag=None):
""" Append lines of information into the `text_info` Text() widget.
If a `str` is passed, the text is simply appended with no newline.
"""
self.text_info.configure(state=tk.NORMAL)
if not isinstance(text, str):
for line in text:
if not line.endswith('\n'):
line = '{}\n'.format(line)
self.append_info(line)
return None

self.text_info.insert(tk.END, text)
if tag:
self.text_info.insert(tk.END, text, tag)
else:
self.text_info.insert(tk.END, text)
self.text_info.configure(state=tk.DISABLED)
return None

def build_info(self):
""" Insert machine/app/runtime info into the text_info Text(). """
d = get_system_info()
avgtime = (d['runtime_secs'] or 1) / (d['runs'] or 1)
debug('Total Time: {}, Runs: {}, Average: {}'.format(
d['runtime_secs'],
d['runs'],
avgtime,
))
self.append_info(
'\n'.join((
' Total Runs: {d[runs]}',
' Total Time: {totaltime}',
' Average Time: {avgtime}',
' Master Files: {d[master_files]}',
' Tiger Files: {d[tiger_files]}',
' Archived Files: {d[archive_files]}',
'Unarchived Files: {d[unarchive_files]}',
' Removed Files: {d[remove_files]}',
' Python Version: {d[python_ver]}',
' Platform:',
' {d[platform]}',
def change_info(self, text, tag=None):
""" Change the text_info Text() widget's text. """
self.clear_info()
self.append_info(text, tag=None)

)).format(
d=d,
avgtime=humantime_fromsecs(avgtime) or 'n/a',
totaltime=humantime_fromsecs(d['runtime_secs']) or 'n/a',
)
)
def clear_info(self):
""" Clear the `text_info` Text() widget. """
self.text_info.configure(state=tk.NORMAL)
self.text_info.delete('0.0', tk.END)
self.text_info.configure(state=tk.DISABLED)

def cmd_lbl_img_click(self, event):
""" Handles lbl_img click. """
if self.img_dimmed:
self.destroy()
self.lbl_clicks += 1
if self.lbl_clicks < 2:
return
# Dim the icon image, for no good reason.
rows = []
for y in range(64):
cols = []
for x in range(64):
cols.append(self.img_icon.get(x, y))
rows.append(cols)

for y, row in enumerate(rows):
for x, col in enumerate(row):
if all(_ == 0 for _ in col):
continue
r, g, b = (max(i - 32, 0) for i in col)
hexval = '#{r:0>2x}{g:0>2x}{b:0>2x}'.format(r=r, g=g, b=b)
self.img_icon.put(hexval.join(('{', '}')), to=(x, y))
self.img_dimmed = True
msgs = [
'Stop doing that.',
'Stop.',
'Seriously, you don\'t know what is going to happen.',
]
msglen = len(msgs)
max_clicks = msglen + 1
if self.lbl_clicks == max_clicks:
self.change_info('Final warning.', tag='error')
elif self.lbl_clicks > max_clicks:
if not self.appending_garbage:
self.append_garbage(callback=self.destroy)
else:
self.change_info(msgs[(self.lbl_clicks - 1) % msglen])

def destroy(self):
debug('Saving gui-about config...')
Expand All @@ -265,3 +288,35 @@ def destroy(self):
super().destroy()
debug('Calling destroy_cb({})...'.format(self.destroy_cb))
handle_cb(self.destroy_cb)

def get_info(self):
""" Get machine/app/runtime info. """
# TODO: Use insert(index, chars, tags) to tag labels/values and
# colorize them?
d = get_system_info()
avgtime = (d['runtime_secs'] or 1) / (d['runs'] or 1)
debug('Total Time: {}, Runs: {}, Average: {}'.format(
d['runtime_secs'],
d['runs'],
avgtime,
))

return '\n'.join((
' Total Runs: {d[runs]}',
' Total Time: {totaltime}',
' Average Time: {avgtime}',
' Master Files: {d[master_files]}',
' Tiger Files: {d[tiger_files]}',
' Archived Files: {d[archive_files]}',
'Unarchived Files: {d[unarchive_files]}',
' Removed Files: {d[remove_files]}',
' Fatal Errors: {d[fatal_errors]}',
' Python Version: {d[python_ver]}',
' Platform:',
' {d[platform]}',

)).format(
d=d,
avgtime=humantime_fromsecs(avgtime) or 'n/a',
totaltime=humantime_fromsecs(d['runtime_secs']) or 'n/a',
)
32 changes: 23 additions & 9 deletions lib/gui/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from ..util.logger import (
debug,
debugprinter,
debug_err,
debug_exc,
debug_obj,
print_err,
Expand Down Expand Up @@ -168,12 +169,11 @@ def __call__(self, *args):
raise
except Exception as ex:
# Log the message, and show an error dialog.
print_err('GUI Error: ({})'.format(type(ex).__name__))
print_err('GUI Error: ({})'.format(
type(ex).__name__,
))
debug_exc()
messagebox.showerror(
title='{} - Error'.format(NAME),
message=str(ex),
)
show_error(ex)
raise


Expand Down Expand Up @@ -218,14 +218,28 @@ def debug_settings(self, level=0):
""" Shortcut for self.debug_attr('settings'). """
return self.debug_attr('settings', level=level + 1)

def show_error(self, msg):
""" Use show_error, but make sure this window is out of the way. """
def show_error(self, msg, fatal=False):
""" Use show_error, but make sure this window is out of the way.
If `fatal` is truthy, call `self.destroy()` afterwards.
"""
old_topmost = self.attributes('-topmost')
self.attributes('-topmost', 0)
self.lower()
if fatal:
self.withdraw()
else:
self.lower()
show_error(msg)
self.lift()
self.attributes('-topmost', old_topmost)
if fatal:
debug_err(
'Closing {} for fatal error:\n{}'.format(
type(self).__name__,
msg,
)
)
self.after_idle(self.destroy, False)
else:
self.lift()


# Use TkErrorLogger
Expand Down
Loading

0 comments on commit 61384b0

Please sign in to comment.