From 1b0a8893c6ef4b41069752422fcd0ca8c6062f46 Mon Sep 17 00:00:00 2001 From: Michael Lazar Date: Thu, 29 Jan 2015 21:41:50 -0800 Subject: [PATCH] Added animated loading box. --- rtv/content_generators.py | 1 + rtv/submission_viewer.py | 21 +++--------- rtv/subreddit_viewer.py | 13 ++++---- rtv/utils.py | 67 +++++++++++++++++++++++++++++++++++++-- rtv/viewer.py | 15 --------- 5 files changed, 76 insertions(+), 41 deletions(-) diff --git a/rtv/content_generators.py b/rtv/content_generators.py index 8a6c802..57608ca 100644 --- a/rtv/content_generators.py +++ b/rtv/content_generators.py @@ -3,6 +3,7 @@ import praw from utils import clean, strip_subreddit_url, humanize_timestamp +# TODO: rename, ... container? class BaseContent(object): @staticmethod diff --git a/rtv/submission_viewer.py b/rtv/submission_viewer.py index 2ebfdd0..37152f7 100644 --- a/rtv/submission_viewer.py +++ b/rtv/submission_viewer.py @@ -3,7 +3,7 @@ import curses import sys from content_generators import SubmissionContent, SubredditContent -from utils import curses_session +from utils import curses_session, LoadScreen from viewer import BaseViewer class SubmissionViewer(BaseViewer): @@ -50,7 +50,8 @@ class SubmissionViewer(BaseViewer): def refresh_content(self): self.add_loading() - self.content.reset() + with LoadScreen(self.stdscr): + self.content.reset() self.stdscr.clear() self.draw() @@ -137,18 +138,4 @@ class SubmissionViewer(BaseViewer): row = len(data['split_title']) + len(data['split_text']) + 3 win.addnstr(row, 1, text, n_cols) - win.border() - -def main(): - - with curses_session() as stdscr: - r = praw.Reddit(user_agent='reddit terminal viewer (rtv) v0.0') - submission = SubredditContent(r).get(0)['object'] - generator = SubmissionContent(submission) - - viewer = SubmissionViewer(stdscr, generator) - viewer.loop() - -if __name__ == '__main__': - - main() \ No newline at end of file + win.border() \ No newline at end of file diff --git a/rtv/subreddit_viewer.py b/rtv/subreddit_viewer.py index 0ef35a2..c134e2b 100644 --- a/rtv/subreddit_viewer.py +++ b/rtv/subreddit_viewer.py @@ -6,7 +6,7 @@ import sys from content_generators import SubredditContent, SubmissionContent from submission_viewer import SubmissionViewer from viewer import BaseViewer -from utils import curses_session, text_input +from utils import curses_session, text_input, LoadScreen class SubredditViewer(BaseViewer): @@ -49,10 +49,10 @@ class SubredditViewer(BaseViewer): def refresh_content(self, subreddit=None): - self.add_loading() self.nav.page_index, self.nav.cursor_index = 0, 0 self.nav.inverted = False - self.content.reset(subreddit=subreddit) + with LoadScreen(self.stdscr): + self.content.reset(subreddit=subreddit) self.stdscr.clear() self.draw() @@ -73,10 +73,9 @@ class SubredditViewer(BaseViewer): def open_submission(self): "Select the current submission to view posts" - self.add_loading() - - submission = self.content.get(self.nav.absolute_index)['object'] - content = SubmissionContent(submission) + with LoadScreen(self.stdscr): + submission = self.content.get(self.nav.absolute_index)['object'] + content = SubmissionContent(submission) viewer = SubmissionViewer(self.stdscr, content) viewer.loop() diff --git a/rtv/utils.py b/rtv/utils.py index f49b873..1211bab 100644 --- a/rtv/utils.py +++ b/rtv/utils.py @@ -1,13 +1,76 @@ -from datetime import datetime, timedelta -from contextlib import contextmanager import os import curses +import time +import threading from curses import textpad +from datetime import datetime, timedelta +from contextlib import contextmanager class EscapePressed(Exception): pass +class LoadScreen(object): + + def __init__( + self, + stdscr, + message='Downloading', + trail='...', + delay=0.5, + interval=0.4): + + self._stdscr = stdscr + self.message = message + self.delay = delay + self.interval=interval + self.trail = trail + + message_len = len(self.message) + len(self.trail) + n_rows, n_cols = stdscr.getmaxyx() + s_row = (n_rows - 2) / 2 + s_col = (n_cols - message_len - 1) / 2 + self.window = stdscr.derwin(3, message_len+2, s_row, s_col) + + self._animator = threading.Thread(target=self.animate) + self._animator.daemon = True + self._is_running = None + + def __enter__(self): + + self._is_running = True + self._animator.start() + + def __exit__(self, exc_type, exc_val, exc_tb): + + self._is_running = False + self._animator.join() + + del self.window + self._stdscr.refresh() + + def animate(self): + + # Delay before popping up the animation to avoid flashing + # the screen if the load time is effectively zero + start = time.time() + while (time.time() - start) < self.delay: + if not self._is_running: + return + + while True: + for i in xrange(len(self.trail)+1): + + if not self._is_running: + return + + self.window.erase() + self.window.border() + self.window.addstr(1, 1, self.message + self.trail[:i]) + self.window.refresh() + time.sleep(self.interval) + + def clean(unicode_string): """ Convert unicode string into ascii-safe characters. diff --git a/rtv/viewer.py b/rtv/viewer.py index 2371da6..9ef9b48 100644 --- a/rtv/viewer.py +++ b/rtv/viewer.py @@ -83,7 +83,6 @@ class BaseViewer(object): self.nav = Navigator(self.content.get, **kwargs) self._subwindows = None - self.add_loading() def draw(self): raise NotImplementedError @@ -107,20 +106,6 @@ class BaseViewer(object): continue self.stdscr.nodelay(0) - def add_loading(self): - "Draw a `loading` popup dialog in the center of the screen" - - message = 'Loading...' - - n_rows, n_cols = self.stdscr.getmaxyx() - win_rows, win_cols = 3, len(message)+2 - start_row = (n_rows - win_rows) / 2 - start_col = (n_cols - win_cols) / 2 - window = self.stdscr.derwin(win_rows, win_cols, start_row, start_col) - window.border() - window.addstr(1, 1, message) - window.refresh() - def draw_header(self): n_rows, n_cols = self._header_window.getmaxyx()