Advertisement
Guest User

Untitled

a guest
Feb 14th, 2017
117
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.72 KB | None | 0 0
  1. import urwid
  2. import webbrowser
  3. import os
  4. from pages import Story, RedditHandler, BadSubredditError, Navigation
  5. from optparse import OptionParser # argparse is 2.7-only
  6.  
  7. # Main loop is global so MainWindow can update the screen asynchronously
  8. main_loop = None
  9.  
  10. class Listing(urwid.FlowWidget):
  11.     """contains a single story and manages its events"""
  12.     def __init__(self, story):
  13.         self.story = story
  14.        
  15.     def selectable(self):
  16.         return True
  17.        
  18.     def rows(self, size, focus=False):
  19.         return 2
  20.  
  21.     def render(self, size, focus=False):
  22.         (maxcol,) = size
  23.         lines = self.story.format_lines(maxcol)
  24.         if focus:
  25.             pass
  26.         # pad lines to column width
  27.         fill = lambda x: x.ljust(maxcol)
  28.         return urwid.TextCanvas(text=list(map(fill, lines)))
  29.  
  30.     def keypress(self, size, key):
  31.         if key in ('o', 'enter'):
  32.             webbrowser.open(self.story.url)
  33.         elif key == 'O':
  34.             if self.story.domain[:5] == "self.":
  35.                 # Lynx renders mobile reddit better
  36.                 url = "http://m.reddit.com" + self.story.permalink
  37.             else:
  38.                 url = self.story.url
  39.             os.system("lynx -accept_all_cookies " + url)
  40.         elif key == 'h':
  41.             webbrowser.open("http://www.reddit.com" + self.story.permalink)
  42.         elif key == 'l':
  43.             url = "http://m.reddit.com" + self.story.permalink
  44.             os.system("lynx -accept_all_cookies " + url)
  45.         else:
  46.             return key
  47.  
  48.  
  49. class MainWindow(object):
  50.     """manages main window elements"""
  51.     def __init__(self):
  52.         # Handles page downloads and cookies                            
  53.         self.handler = RedditHandler()
  54.         self.listings = []
  55.         self.__subreddit = None
  56.         self.nav = None
  57.         self.__load_stories()
  58.        
  59.         # Prep header and footer ui widgets
  60.         self.__update_header()
  61.         self.footer_content = urwid.Text(('footer', ""), wrap='clip')
  62.         self.footer = urwid.Padding(self.footer_content, left=1, right=1)
  63.        
  64.         self.frame = urwid.Frame(   self.__get_widget(),
  65.                                     header=self.header,
  66.                                     footer=self.footer )
  67.  
  68.    
  69.     def login(self, username, password):
  70.         """attempt to login"""
  71.         login_result = self.handler.login(username, password)
  72.         if login_result:
  73.             self.__update_header()
  74.         return login_result
  75.      
  76.        
  77.     def set_subreddit(self, subreddit):
  78.         """switch subreddits"""
  79.         self.nav = None
  80.         old_subreddit = self.__subreddit
  81.         if subreddit == "fp":
  82.             self.__subreddit = None
  83.         else:
  84.             self.set_status("Loading subreddit: /r/{0}".format(subreddit))
  85.             self.__subreddit = subreddit
  86.         try:
  87.             self.__load_stories()
  88.         except BadSubredditError:
  89.             self.set_status("Error loading subreddit /r/{0}!".format(subreddit))
  90.             self.__subreddit = old_subreddit
  91.             self.__load_stories()
  92.         main_widget = self.__get_widget()
  93.         self.frame.set_body(main_widget)
  94.         self.set_status()
  95.    
  96.     def __update_header(self):
  97.         """set the titlebar to the currently logged in user"""
  98.         if self.handler.user:
  99.             header_text = "[{0}] - reddit-cli - github.com/cev/reddit-cli".format(self.handler.user)
  100.         else:
  101.             header_text = "reddit-cli - github.com/cev/reddit-cli"
  102.         self.header = urwid.Text(('header',
  103.                                 header_text),
  104.                                 align='center')
  105.         if hasattr(self, 'frame'):
  106.             self.frame.set_header(self.header)                                  
  107.  
  108.     def __load_stories(self, direction=None):
  109.         """load stories from (sub)reddit and store Listings"""
  110.         self.listings = []
  111.         data = self.handler.download_stories(self.__subreddit, self.nav, direction)
  112.        
  113.         self.nav = data[1]
  114.         for s in data[0]:
  115.             current = Listing(s)
  116.             self.listings.append(urwid.Padding(current, left=1, right=1))
  117.        
  118.     def __get_widget(self):
  119.         """return TextBox widget containing all Listings"""
  120.         listings_formatted = self.listings[:]
  121.            
  122.         # Separate stories with blank line & highlight on focus
  123.         for (i, l) in enumerate(listings_formatted):
  124.             filled = urwid.Filler(urwid.AttrMap(l, None, 'focus'))
  125.             listings_formatted[i] = urwid.BoxAdapter(filled, 3)
  126.         listings_formatted.append(urwid.Divider("*"))
  127.        
  128.         self.listings_active = urwid.ListBox(urwid.SimpleListWalker(listings_formatted))
  129.         return self.listings_active
  130.        
  131.     def __format_status(self):
  132.         """format status text for use in footer"""
  133.         if self.__subreddit is None:
  134.             subreddit_text = "/r/front_page"
  135.         else:
  136.             subreddit_text = "/r/" + self.__subreddit
  137.         status = "[{0}] ({1}) ?: help n/m:pagination".format(self.nav.count/25+1, subreddit_text)
  138.         return status
  139.    
  140.     def switch_page(self, direction):
  141.         """load stories from the previous or next page"""
  142.         if direction == "prev":
  143.             self.set_status("(<) Loading...")
  144.             self.__load_stories(direction=direction)
  145.         elif direction == "next":
  146.             self.set_status("(>) Loading...")
  147.             self.__load_stories(direction=direction)
  148.         else:
  149.             raise Exception('Direction must be "prev" or "next"')
  150.         main_widget = self.__get_widget()
  151.         self.frame.set_body(main_widget)
  152.         self.set_status()
  153.    
  154.     def set_status(self, message=None):
  155.         """write message on footer or else default status string"""
  156.         if message is None:
  157.             status = self.__format_status()
  158.         else:
  159.             status = message
  160.         self.footer_content.set_text(('footer', status))
  161.        
  162.         global main_loop
  163.         if not main_loop is None:
  164.             main_loop.draw_screen()
  165.        
  166.     def refresh(self):
  167.         """reload stories in main window"""
  168.         self.set_status("Reloading...")
  169.         self.nav = None
  170.         try:
  171.             self.__load_stories()
  172.         except BadSubredditError:
  173.             self.set_status("Error loading subreddit!")
  174.             return
  175.         main_widget = self.__get_widget()
  176.         self.frame.set_body(main_widget)
  177.         self.set_status()
  178.        
  179.            
  180.  
  181. def main():
  182.     palette =   [
  183.                 ('header', 'dark magenta,bold', 'default'),
  184.                 ('footer', 'black', 'light gray'),
  185.                 ('textentry', 'white,bold', 'dark red'),
  186.                 ('body', 'light gray', 'default'),
  187.                 ('focus', 'black', 'dark cyan', 'standout')
  188.                 ]
  189.  
  190.     textentry = urwid.Edit()
  191.     assert textentry.get_text() == ('', []), textentry.get_text()  
  192.    
  193.     parser = OptionParser()
  194.     parser.add_option("-u", "--username")
  195.     parser.add_option("-p", "--password")
  196.     (options, args) = parser.parse_args()
  197.    
  198.     if options.username and not options.password:
  199.         print('If you specify a username, you must also specify a password')
  200.         exit()
  201.        
  202.     print('Loading...')
  203.    
  204.     body = MainWindow()
  205.     if options.username:
  206.         print('[Logging in]')
  207.         if body.login(options.username, options.password):
  208.             print ('[Login Successful]')
  209.         else:
  210.             print ('[Login Failed]')
  211.             exit()
  212.            
  213.     body.refresh()
  214.        
  215.     def edit_handler(keys, raw):
  216.         """respond to keys while user is editing text"""      
  217.         if keys in (['enter'],[]):
  218.             if keys == ['enter']:
  219.                 if textentry.get_text()[0] != '':
  220.                     # We set the footer twice because the first time we
  221.                     # want the updated status text (loading...) to show
  222.                     # immediately, and the second time as a catch-all
  223.                     body.frame.set_footer(body.footer)
  224.                     body.set_subreddit(textentry.edit_text)
  225.                     textentry.set_edit_text('')
  226.             # Restore original status footer
  227.             body.frame.set_footer(body.footer)
  228.             body.frame.set_focus('body')
  229.             global main_loop
  230.             main_loop.input_filter = input_handler
  231.             return
  232.         return keys
  233.        
  234.     def input_handler(keys, raw):
  235.         """respond to keys not handled by a specific widget"""
  236.         for key in keys:
  237.             if key == 's':
  238.                 # Replace status footer wth edit widget
  239.                 textentry.set_caption(('textentry', ' [subreddit?] ("fp" for the front page) :>'))
  240.                 body.frame.set_footer(urwid.Padding(textentry, left=4))
  241.                 body.frame.set_focus('footer')
  242.                 global main_loop
  243.                 main_loop.input_filter = edit_handler
  244.                 return
  245.             elif key in ('j','k'):
  246.                 direction = 'down' if key == 'j' else 'up'
  247.                 return [direction]
  248.             elif key in ('n','m'):
  249.                 direction = 'prev' if key == 'n' else 'next'
  250.                 body.switch_page(direction)
  251.             elif key == 'u':
  252.                 body.refresh()
  253.             elif key == 'b': # boss mode
  254.                 os.system("man python")
  255.             elif key == '?': # help mode
  256.                 os.system("less -Ce README.markdown")
  257.             elif key == 'q': # quit
  258.                 raise urwid.ExitMainLoop()
  259.             return keys
  260.  
  261.     # Start ui
  262.     global main_loop
  263.     main_loop = urwid.MainLoop(body.frame, palette, input_filter=input_handler)
  264.     main_loop.run()
  265.  
  266. if __name__ == "__main__":
  267.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement