From 7b1f2f94822e6d27f00687ab1f12d36453339d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Cort=C3=A9z?= Date: Mon, 19 Jan 2015 12:15:57 -0600 Subject: [PATCH] Basic invisible interface support --- src/app-configuration.defaults | 20 ++-- src/application.py | 8 +- src/controller/buffersController.py | 20 ++-- src/controller/mainController.py | 162 +++++++++++++++++++++++++++- src/main.py | 10 +- src/mysc/thread_utils.py | 4 +- src/widgetUtils/wxUtils.py | 2 +- src/wxUI/view.py | 14 +++ 8 files changed, 210 insertions(+), 30 deletions(-) diff --git a/src/app-configuration.defaults b/src/app-configuration.defaults index 55ef9ef8..1912b5dd 100644 --- a/src/app-configuration.defaults +++ b/src/app-configuration.defaults @@ -12,25 +12,29 @@ input_device = string(default="Default") output_device = string(default="Default") global_mute = boolean(default=False) current_soundpack = string(default="default") +ask_at_exit = boolean(default=True) +use_invisible_keyboard_shorcuts = boolean(default=False) [keymap] up = string(default="control+win+up") down = string(default="control+win+down") left = string(default="control+win+left") right = string(default="control+win+right") +next_account = string(default="control+win+shift+right") +previous_account = string(default="control+win+shift+left") conversation_up = string(default="control+win+shift+up") conversation_down = string(default="control+win+shift+down") show_hide = string(default="control+win+m") -compose = string(default="control+win+n") -reply = string(default="control+win+r") -retweet = string(default="control+win+shift+r") -dm = string(default="control+win+d") -fav = string(default="alt+win+f") -unfav = string(default="alt+shift+win+f") +post_tweet = string(default="control+win+n") +post_reply = string(default="control+win+r") +post_retweet = string(default="control+win+shift+r") +send_dm = string(default="control+win+d") +add_to_favourites = string(default="alt+win+f") +remove_from_favourites = string(default="alt+shift+win+f") action = string(default="control+win+s") details = string(default="control+win+alt+n") -view = string(default="control+win+v") -close = string(default="control+win+f4") +view_item = string(default="control+win+v") +exit = string(default="control+win+f4") open_timeline = string(default="control+win+i") delete_buffer = string(default="control+win+shift+i") url = string(default="control+win+return") diff --git a/src/application.py b/src/application.py index 7dbcf3ca..2735b54c 100644 --- a/src/application.py +++ b/src/application.py @@ -2,11 +2,11 @@ name = 'TW Blue' snapshot = False if snapshot == False: - version = "0.47" - update_url = 'http://twblue.com.mx/updates/tw_blue.json' + version = "0.90" + update_url = 'http://twblue.es/updates/tw_blue.json' else: - version = "4" - update_url = 'http://twblue.com.mx/updates/snapshots.json' + version = "7" + update_url = 'http://twblue.es/updates/snapshots.json' author = u"Manuel Cortéz" authorEmail = "info@twblue.com.mx" copyright = u"copyright (C) 2013-2014, Manuel cortéz" diff --git a/src/controller/buffersController.py b/src/controller/buffersController.py index ee41253a..17e06112 100644 --- a/src/controller/buffersController.py +++ b/src/controller/buffersController.py @@ -25,6 +25,7 @@ class bufferController(object): self.buffer = None self.account = "" self.needs_init = True + self.invisible = False # False if the buffer will be ignored on the invisible interface. def get_event(self, ev): if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio" @@ -148,6 +149,7 @@ class baseBufferController(bufferController): self.buffer = getattr(buffers, bufferType)(parent, name) else: self.buffer = buffers.basePanel(parent, name) + self.invisible = True self.name = name self.type = self.buffer.type self.id = self.buffer.GetId() @@ -159,7 +161,7 @@ class baseBufferController(bufferController): self.bind_events() def get_message(self): - return " ".join(self.compose_function(self.get_right_tweet(), self.session.db, self.session.settings["general"]["relative_times"])[1:-2]) + return " ".join(self.compose_function(self.get_right_tweet(), self.session.db, self.session.settings["general"]["relative_times"])) def start_stream(self): log.debug("Starting stream for buffer %s, account %s and type %s" % (self.name, self.account, self.type)) @@ -261,7 +263,7 @@ class baseBufferController(bufferController): tweet = self.get_tweet() if self.session.settings["general"]["relative_times"] == True: # fix this: - original_date = arrow.get(self.session.db[self.name_buffer][self.list.get_selected()]["created_at"], "ddd MMM D H:m:s Z YYYY", locale="en") + original_date = arrow.get(self.session.db[self.name][self.buffer.list.get_selected()]["created_at"], "ddd MMM D H:m:s Z YYYY", locale="en") ts = original_date.humanize(locale=languageHandler.getLanguage()) self.buffer.list.list.SetStringItem(self.buffer.list.get_selected(), 2, ts) if utils.is_audio(tweet): @@ -319,6 +321,7 @@ class eventsBufferController(bufferController): def __init__(self, parent, name, session, account, *args, **kwargs): super(eventsBufferController, self).__init__(parent, *args, **kwargs) log.debug("Initializing buffer %s, account %s" % (name, account,)) + self.invisible = True self.buffer = buffers.eventsPanel(parent, name) self.name = name self.account = account @@ -329,12 +332,9 @@ class eventsBufferController(bufferController): self.type = self.buffer.type def get_message(self): - if self.list.get_count() == 0: return _(u"Empty") + if self.buffer.list.get_count() == 0: return _(u"Empty") # fix this: - if platform.system() == "Windows": - return "%s. %s" % (self.buffer.list.list.GetItemText(self.buffer.list.get_selected()), self.buffer.list.list.GetItemText(self.buffer.list.get_selected(), 1)) - else: - return self.buffer.list.list.GetStringSelection() + return "%s. %s" % (self.buffer.list.list.GetItemText(self.buffer.list.get_selected()), self.buffer.list.list.GetItemText(self.buffer.list.get_selected(), 1)) def add_new_item(self, item): tweet = self.compose_function(item, self.session.db["user_name"]) @@ -348,7 +348,7 @@ class peopleBufferController(baseBufferController): super(peopleBufferController, self).__init__(parent, function, name, sessionObject, account, bufferType="peoplePanel") log.debug("Initializing buffer %s, account %s" % (name, account,)) self.compose_function = compose.compose_followers_list - log.debug("Compose_function: self.compose_function" % (self.compose_function,)) + log.debug("Compose_function: %s" % (self.compose_function,)) self.get_tweet = self.get_right_tweet def onFocus(self, ev): @@ -364,12 +364,12 @@ class peopleBufferController(baseBufferController): log.debug("args: %s, kwargs: %s" % (self.args, self.kwargs)) val = self.session.get_cursored_stream(self.name, self.function, *self.args, **self.kwargs) # self.session.order_cursored_buffer(self.name, self.session.db[self.name]) - log.debug("Number of items retrieved: %d" % (val,)) +# log.debug("Number of items retrieved: %d" % (val,)) self.put_items_on_list(val) def put_items_on_list(self, number_of_items): log.debug("The list contains %d items" % (self.buffer.list.get_count(),)) - log.debug("Putting %d items on the list..." % (number_of_items,)) +# log.debug("Putting %d items on the list..." % (number_of_items,)) if self.buffer.list.get_count() == 0: for i in self.session.db[self.name]["items"]: tweet = self.compose_function(i, self.session.db, self.session.settings["general"]["relative_times"]) diff --git a/src/controller/mainController.py b/src/controller/mainController.py index c0877db6..1350aa2c 100644 --- a/src/controller/mainController.py +++ b/src/controller/mainController.py @@ -11,14 +11,19 @@ from mysc.thread_utils import call_threaded from mysc.repeating_timer import RepeatingTimer import config import widgetUtils +import pygeocoder +from pygeolib import GeocoderError import platform from extra import SoundsTutorial import logging if platform.system() == "Windows": import keystrokeEditor + from keyboard_handler.wx_handler import WXKeyboardHandler log = logging.getLogger("mainController") +geocoder = pygeocoder.Geocoder() + class Controller(object): """ Main Controller for TWBlue. It manages the main window and sessions.""" @@ -49,6 +54,18 @@ class Controller(object): buffer = self.search_buffer(view_buffer.name, view_buffer.account) return buffer + def get_first_buffer(self, account): + for i in self.buffers: + if i.account == account: + buff = i + break + return self.view.search(buff.name, buff.account) + + def get_last_buffer(self, account): + results = [] + [results.append(i) for i in self.buffers if i.account == account] + return self.view.search(results[-1].name, results[-1].account) + def bind_stream_events(self): log.debug("Binding events for the Twitter stream API...") pub.subscribe(self.manage_home_timelines, "item-in-home") @@ -71,6 +88,7 @@ class Controller(object): log.debug("Binding other application events...") pub.subscribe(self.editing_keystroke, "editing_keystroke") pub.subscribe(self.manage_stream_errors, "stream-error") + widgetUtils.connect_event(self.view, widgetUtils.MENU, self.show_hide, menuitem=self.view.show_hide) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.search, menuitem=self.view.menuitem_search) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.learn_sounds, menuitem=self.view.sounds_tutorial) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.exit, menuitem=self.view.close) @@ -82,11 +100,16 @@ class Controller(object): widgetUtils.connect_event(self.view, widgetUtils.MENU, self.view_item, self.view.view) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.delete, self.view.delete) widgetUtils.connect_event(self.view, widgetUtils.MENU, self.send_dm, self.view.dm) + widgetUtils.connect_event(self.view.nb, widgetUtils.NOTEBOOK_PAGE_CHANGED, self.buffer_changed) def __init__(self): super(Controller, self).__init__() + self.showing = True self.view = view.mainFrame() self.buffers = [] + self.accounts = [] + self.buffer_positions = {} + self.current_account = "" self.view.prepare() self.bind_stream_events() self.bind_other_events() @@ -104,6 +127,8 @@ class Controller(object): def create_buffers(self, session): session.get_user_info() + self.accounts.append(session.db["user_name"]) + self.buffer_positions[session.db["user_name"]] = 1 account = buffersController.accountPanel(self.view.nb, session.db["user_name"], session.db["user_name"]) self.buffers.append(account) self.view.add_buffer(account.buffer , name=session.db["user_name"]) @@ -259,7 +284,7 @@ class Controller(object): else: buffer.reply() - def send_dm(self, user): + def send_dm(self, *args, **kwargs): buffer = self.get_current_buffer() if buffer.name == "sent_direct_messages" or buffer.name == "sent-tweets": return else: @@ -309,8 +334,18 @@ class Controller(object): def remove_buffer(self): pass - def show_hide(self): - pass + def show_hide(self, *args, **kwargs): + km = self.create_invisible_keyboard_shorcuts() + if self.showing == True: + if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] == False: + self.register_invisible_keyboard_shorcuts(km) + self.view.Hide() + self.showing = False + else: + if config.app["app-settings"]["use_invisible_keyboard_shorcuts"] == False: + self.unregister_invisible_keyboard_shorcuts(km) + self.view.Show() + self.showing = True def toggle_global_mute(self): pass @@ -321,8 +356,125 @@ class Controller(object): def toggle_autoread(self): pass - def go_conversation(self, orientation): - pass + def skip_buffer(self, forward=True): + buff = self.get_current_buffer() + if buff.invisible == False: + self.view.advance_selection(forward) + + def buffer_changed(self, *args, **kwargs): + if self.get_current_buffer().account != self.current_account: self.current_account = self.get_current_buffer().account + + def up(self, *args, **kwargs): + page = self.get_current_buffer() + position = page.buffer.list.get_selected() + index = position-1 + try: + page.buffer.list.select_item(index) + except: + pass + if position == page.buffer.list.get_selected(): + sound.player.play("limit.ogg") + try: + output.speak(page.get_message()) + except: + pass + + def down(self, *args, **kwargs): + page = self.get_current_buffer() + position = page.buffer.list.get_selected() + index = position+1 + try: + page.buffer.list.select_item(index) + except: + pass + if position == page.buffer.list.get_selected(): + sound.player.play("limit.ogg") + try: + output.speak(page.get_message()) + except: + pass + + def left(self, *args, **kwargs): + buff = self.view.get_current_buffer_pos() + buffer = self.get_current_buffer() + if buff == self.get_first_buffer(buffer.account) or buff == 0: + self.view.change_buffer(self.get_last_buffer(buffer.account)) + else: + self.view.change_buffer(buff-1) + while self.get_current_buffer().invisible == False: self.skip_buffer(False) + buffer = self.get_current_buffer() + try: + msg = _(u"%s, %s of %s") % (self.view.get_buffer_text(), buffer.buffer.list.get_selected()+1, buffer.buffer.list.get_count()) + except: + msg = _(u"%s. Empty") % (self.view.get_buffer_text(),) + output.speak(msg) + + def right(self, *args, **kwargs): + buff = self.view.get_current_buffer_pos() + buffer = self.get_current_buffer() + if buff == self.get_last_buffer(buffer.account) or buff+1 == self.view.get_buffer_count(): + self.view.change_buffer(self.get_first_buffer(buffer.account)) + else: + self.view.change_buffer(buff+1) + while self.get_current_buffer().invisible == False: self.skip_buffer(True) + buffer = self.get_current_buffer() + try: + msg = _(u"%s, %s of %s") % (self.view.get_buffer_text(), buffer.buffer.list.get_selected()+1, buffer.buffer.list.get_count()) + except: + msg = _(u"%s. Empty") % (self.view.get_buffer_text(),) + output.speak(msg) + + def next_account(self, *args, **kwargs): + index = self.accounts.index(self.current_account) + if index+1 == len(self.accounts): + index = 0 + else: + index = index+1 + account = self.accounts[index] + self.current_account = account + buff = self.view.search("home_timeline", account) + self.view.change_buffer(buff) + buffer = self.get_current_buffer() + try: + msg = _(u"%s. %s, %s of %s") % (buffer.account, self.view.get_buffer_text(), buffer.buffer.list.get_selected()+1, buffer.buffer.list.get_count()) + except: + msg = _(u"%s. Empty") % (self.view.get_buffer_text(),) + output.speak(msg) + + def previous_account(self, *args, **kwargs): + index = self.accounts.index(self.current_account) + if index-1 < 0: + index = len(self.accounts)-1 + else: + index = index-1 + account = self.accounts[index] + self.current_account = account + buff = self.view.search("home_timeline", account) + self.view.change_buffer(buff) + buffer = self.get_current_buffer() + try: + msg = _(u"%s. %s, %s of %s") % (buffer.account, self.view.get_buffer_text(), buffer.buffer.list.get_selected()+1, buffer.buffer.list.get_count()) + except: + msg = _(u"%s. Empty") % (self.view.get_buffer_text(),) + output.speak(msg) + + def create_invisible_keyboard_shorcuts(self): + keymap = {} + for i in config.app["keymap"]: + if hasattr(self, i): + keymap[config.app["keymap"][i]] = getattr(self, i) + return keymap + + def register_invisible_keyboard_shorcuts(self, keymap): + self.keyboard_handler = WXKeyboardHandler(self.view) + self.keyboard_handler.register_keys(keymap) + + def unregister_invisible_keyboard_shorcuts(self, keymap): + try: + self.keyboard_handler.unregister_keys(keymap) + del self.keyboard_handler + except AttributeError: + pass def notify(self, play_sound=None, message=None, notification=False): if play_sound != None: diff --git a/src/main.py b/src/main.py index 254a1abb..56d5ffef 100644 --- a/src/main.py +++ b/src/main.py @@ -6,10 +6,18 @@ import commandline import config import sound import output -from logger import logger as logging +from logger import logger +import logging +import platform +import application +log = logging.getLogger("main") def setup(): + log.debug("Starting TWBlue %s" % (application.version,)) config.setup() + log.debug("Using %s %s" % (platform.system(), platform.architecture()[0])) + log.debug("Application path is %s" % (paths.app_path(),)) + log.debug("Data path is %s" % (paths.data_path(),)) sound.setup() output.setup() languageHandler.setLanguage(config.app["app-settings"]["language"]) diff --git a/src/mysc/thread_utils.py b/src/mysc/thread_utils.py index c37099f7..f400516b 100644 --- a/src/mysc/thread_utils.py +++ b/src/mysc/thread_utils.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +import logging +log = logging.getLogger("mysc.thread_utils") import threading import wx from pubsub import pub @@ -13,7 +15,7 @@ def call_threaded(func, *args, **kwargs): except TwythonRateLimitError: pass except: - logging.exception("Thread %d with function %r, args of %r, and kwargs of %r failed to run." % (threading.current_thread().ident, func, a, k)) + log.exception("Thread %d with function %r, args of %r, and kwargs of %r failed to run." % (threading.current_thread().ident, func, a, k)) # pass thread = threading.Thread(target=new_func, args=args, kwargs=kwargs) thread.daemon = True diff --git a/src/widgetUtils/wxUtils.py b/src/widgetUtils/wxUtils.py index 8ee968d4..e08094dd 100644 --- a/src/widgetUtils/wxUtils.py +++ b/src/widgetUtils/wxUtils.py @@ -22,7 +22,7 @@ ENTERED_TEXT = wx.EVT_TEXT MENU = wx.EVT_MENU KEYPRESS = wx.EVT_CHAR_HOOK KEYUP = wx.EVT_KEY_UP -NOTEBOOK_PAGE_CHANGED = wx.EVT_NOTEBOOK_PAGE_CHANGED +NOTEBOOK_PAGE_CHANGED = wx.EVT_TREEBOOK_PAGE_CHANGED def exit_application(): """ Closes the current window cleanly. """ wx.GetApp().ExitMainLoop() diff --git a/src/wxUI/view.py b/src/wxUI/view.py index 79cb1397..10d31bd3 100644 --- a/src/wxUI/view.py +++ b/src/wxUI/view.py @@ -104,6 +104,9 @@ class mainFrame(wx.Frame): self.nb = wx.Treebook(self.panel, wx.NewId()) self.buffers = {} + def get_buffer_count(self): + return self.nb.GetPageCount() + def add_buffer(self, buffer, name): self.nb.AddPage(buffer, name) self.buffers[name] = buffer.GetId() @@ -124,11 +127,22 @@ class mainFrame(wx.Frame): def get_current_buffer(self): return self.nb.GetCurrentPage() + def get_current_buffer_pos(self): + return self.nb.GetSelection() + def get_buffer(self, pos): return self.GetPage(pos) + def change_buffer(self, position): + self.nb.ChangeSelection(position) + + def get_buffer_text(self): + return self.nb.GetPageText(self.nb.GetSelection()) + def get_buffer_by_id(self, id): return self.nb.FindWindowById(id) + def advance_selection(self, forward): + self.nb.AdvanceSelection(forward) def show(self): self.Show() \ No newline at end of file