From 0ba1aadf70087ed815fcea6e5ce1410545e6eb95 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 17 Apr 2018 21:08:22 +0300 Subject: [PATCH] app.py and main.py refactoring and fixes --- toxygen/app.py | 194 +++++++++++++++++--------------- toxygen/main.py | 14 +-- toxygen/middleware/callbacks.py | 38 ++++--- toxygen/middleware/threads.py | 38 +++---- toxygen/user_data/settings.py | 18 ++- 5 files changed, 160 insertions(+), 142 deletions(-) diff --git a/toxygen/app.py b/toxygen/app.py index 3668c4b..85da363 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -1,6 +1,7 @@ from middleware import threads +import middleware.callbacks as callbacks from PyQt5 import QtWidgets, QtGui, QtCore -import ui.password_screen as passwordscreen +import ui.password_screen as password_screen from util.util import * import updater.updater as updater import os @@ -20,32 +21,30 @@ class App: def __init__(self, version, path_to_profile=None, uri=None): self._version = version - self._app = None - self._tox = self._ms = self._init = self._app = self.tray = self._main_loop = self._av_loop = None - self.uri = self._toxes = self._tray = None + self._app = self._settings = self._profile_manager = self._plugin_loader = None + self._tox = self._ms = self._init = self.tray = self._main_loop = self._av_loop = None + self._uri = self._toxes = self._tray = None if uri is not None and uri.startswith('tox:'): - self.uri = uri[4:] + self._uri = uri[4:] self._path = path_to_profile def enter_pass(self, data): """ Show password screen """ - p = passwordscreen.PasswordScreen(self._toxes, data) + p = password_screen.PasswordScreen(self._toxes, data) p.show() self._app.lastWindowClosed.connect(self._app.quit) self._app.exec_() - result = p.result - if result is None: - raise SystemExit() - else: - return result + if p.result is not None: + return p.result + raise SystemExit() def main(self): """ Main function of app. loads login screen if needed and starts main screen """ - self._app= QtWidgets.QApplication([]) + self._app = QtWidgets.QApplication([]) icon_file = os.path.join(get_images_directory(), 'icon.png') self._app.setWindowIcon(QtGui.QIcon(icon_file)) @@ -59,28 +58,13 @@ class App: encrypt_save = tox_encrypt_save.ToxEncryptSave() self._toxes = user_data.toxes.ToxES(encrypt_save) - if self._path is not None: - path = os.path.dirname(self._path) + '/' - name = os.path.basename(self._path)[:-4] - self._settings = Settings(self._toxes, self._path.replace('.tox', '.json')) - self._profile_manager = ProfileManager(self._settings, self._toxes, path) - data = self._profile_manager.open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - self._tox = self.create_tox(data) + if self._path is not None: # toxygen was started with path to profile + self.load_existing_profile(self._path) else: auto_profile = Settings.get_auto_profile() - if not auto_profile[0]: + if auto_profile is None: # no default profile # show login screen if default profile not found - current_locale = QtCore.QLocale() - curr_lang = current_locale.languageToString(current_locale.language()) - langs = Settings.supported_languages() - if curr_lang in langs: - lang_path = langs[curr_lang] - translator = QtCore.QTranslator() - translator.load(get_translations_directory() + lang_path) - self._app.installTranslator(translator) - self._app.translator = translator + self.load_login_screen_translations() ls = LoginScreen() ls.setWindowIconText("Toxygen") profiles = ProfileManager.find_profiles() @@ -90,59 +74,25 @@ class App: result = ls.result if result is None: return - elif result.is_new_profile(): # create new profile - name = get_profile_name_from_path(result.profile_path) or 'toxygen_user' - pr = map(lambda x: x[1], ProfileManager.find_profiles()) - if name in list(pr): - util_ui.message_box(util_ui.tr('Profile with this name already exists'), - util_ui.tr('Error')) - return - self._tox = tox_factory() - self._tox.self_set_name(bytes(name, 'utf-8') if name else b'Toxygen User') - self._tox.self_set_status_message(b'Toxing on Toxygen') - # TODO: set profile password - path = result.profile_path - self._profile_manager = ProfileManager(self._toxes, path) - try: - self._profile_manager.save_profile(self._tox.get_savedata()) - except Exception as ex: - print(str(ex)) - log('Profile creation exception: ' + str(ex)) - text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?') - util_ui.message_box(text, util_ui.tr('Error')) - return - path = Settings.get_default_path() - self._settings = Settings() - if curr_lang in langs: - self._settings['language'] = curr_lang - self._settings.save() + if result.is_new_profile(): # create new profile + self.create_new_profile(result.profile_path) else: # load existing profile - path = result.profile_path - if result.load_as_default: - Settings.set_auto_profile(path) - self._settings = Settings(self._toxes, path.replace('.tox', '.json')) - self._profile_manager = ProfileManager(self._settings, self._toxes, path) - data = self._profile_manager.open_profile() - if self._toxes.is_data_encrypted(data): - data = self.enter_pass(data) - self._tox = self.create_tox(data) - else: + self.load_existing_profile(result.profile_path) + self._path = result.profile_path + else: # default profile path, name = auto_profile - self._settings = Settings(self._toxes, path + name + '.json') - self._profile_manager = ProfileManager(self._settings, self._toxes, path) - data = self._profile_manager.open_profile() - if encrypt_save.is_data_encrypted(data): - data = self.enter_pass(data) - self.tox = self.create_tox(data) + self._path = os.path.join(path, name + '.tox') + self.load_existing_profile(self._path) - if Settings.is_active_profile(path, get_profile_name_from_path(path)): # profile is in use - title = util_ui.tr('Profile {}').format(name) + if Settings.is_active_profile(self._path): # profile is in use + profile_name = get_profile_name_from_path(self._path) + title = util_ui.tr('Profile {}').format(profile_name) text = util_ui.tr('Other instance of Toxygen uses this profile or profile was not properly closed. Continue?') reply = util_ui.question(text, title) if not reply: return - else: - self._settings.set_active_profile() + + self._settings.set_active_profile() self.load_app_styles() self.load_app_translations() @@ -151,17 +101,21 @@ class App: return self._ms = MainWindow(self._settings, self._tox, self.reset, self._tray) - self._profile = self._ms.profile + profile = self._ms.profile self._ms.show() - self._tray = tray.init_tray(self._profile, self._settings, self._ms) + self._tray = tray.init_tray(profile, self._settings, self._ms) self._tray.show() - self._plugin_loader = PluginLoader(self._tox, self._toxes, self._profile, self._settings) # plugins support - self._plugin_loader.load() # TODO; move to separate thread? + self._plugin_loader = PluginLoader(self._tox, self._toxes, profile, self._settings) # plugins support + self.start_threads() - if self.uri is not None: - self._ms.add_contact(self.uri) + # callbacks initialization + callbacks.init_callbacks(self._tox, profile, self._settings, self._plugin_loader, None, None, None, + self._ms, self._tray) + + if self._uri is not None: + self._ms.add_contact(self._uri) self._app.lastWindowClosed.connect(self._app.quit) self._app.exec_() @@ -169,8 +123,7 @@ class App: self._plugin_loader.stop() self.stop_threads() self._tray.hide() - data = self._tox.get_savedata() - self._profile_manager.save_profile(data) + self.save_profile() self._settings.close() del self._tox @@ -181,10 +134,10 @@ class App: """ self.stop_threads() data = self._tox.get_savedata() - self._profile_manager.save_profile(data) + self.save_profile(data) del self._tox # create new tox instance - self._tox = tox_factory(data, self._settings) + self._tox = self.create_tox(data) self.start_threads() self._plugin_loader.set_tox(self._tox) @@ -199,25 +152,41 @@ class App: style = fl.read() self._app.setStyleSheet(style) + def load_login_screen_translations(self): + current_language, supported_languages = self.get_languages() + if current_language in supported_languages: + lang_path = supported_languages[current_language] + translator = QtCore.QTranslator() + translator.load(get_translations_directory() + lang_path) + self._app.installTranslator(translator) + self._app.translator = translator + + @staticmethod + def get_languages(): + current_locale = QtCore.QLocale() + curr_language = current_locale.languageToString(current_locale.language()) + supported_languages = Settings.supported_languages() + + return curr_language, supported_languages + def load_app_translations(self): lang = Settings.supported_languages()[self._settings['language']] translator = QtCore.QTranslator() - translator.load(curr_directory(__file__) + '/translations/' + lang) + translator.load(os.path.join(get_translations_directory(), lang)) self._app.installTranslator(translator) self._app.translator = translator def try_to_update(self): updating = updater.start_update_if_needed(self._version, self._settings) if updating: - data = self._tox.get_savedata() - self._profile_manager.save_profile(data) + self.save_profile() self._settings.close() del self._tox return updating def start_threads(self): # init thread - self._init = threads.InitThread(self._tox, self._ms, self._tray) + self._init = threads.InitThread(self._tox, self._plugin_loader) self._init.start() # starting threads for tox iterate and toxav iterate @@ -226,11 +195,52 @@ class App: self._av_loop = threads.ToxAVIterateThread(self._tox.AV) self._av_loop.start() + threads.start_file_transfer_thread() + def stop_threads(self): self._init.stop_thread() - self._main_loop.stop_thread() self._av_loop.stop_thread() + self._main_loop.stop_thread() + + threads.stop_file_transfer_thread() def create_tox(self, data): return tox_factory(data, self._settings) + + def load_existing_profile(self, profile_path): + self._settings = Settings(self._toxes, profile_path.replace('.tox', '.json')) + self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) + data = self._profile_manager.open_profile() + if self._toxes.is_data_encrypted(data): + data = self.enter_pass(data) + self._tox = self.create_tox(data) + + def create_new_profile(self, profile_path): + name = get_profile_name_from_path(profile_path) or 'toxygen_user' + if os.path.isfile(profile_path): + util_ui.message_box(util_ui.tr('Profile with this name already exists'), + util_ui.tr('Error')) + return + self._tox = tox_factory() + self._tox.self_set_name(bytes(name, 'utf-8') if name else b'Toxygen User') + self._tox.self_set_status_message(b'Toxing on Toxygen') + # TODO: set profile password + self._settings = Settings(self._toxes, self._path.replace('.tox', '.json')) + self._profile_manager = ProfileManager(self._settings, self._toxes, profile_path) + try: + self.save_profile() + except Exception as ex: + print(ex) + log('Profile creation exception: ' + str(ex)) + text = util_ui.tr('Profile saving error! Does Toxygen have permission to write to this directory?') + util_ui.message_box(text, util_ui.tr('Error')) + return + current_language, supported_languages = self.get_languages() + if current_language in supported_languages: + self._settings['language'] = current_language + self._settings.save() + + def save_profile(self, data=None): + data = data or self._tox.get_savedata() + self._profile_manager.save_profile(data) diff --git a/toxygen/main.py b/toxygen/main.py index 97a5363..cdaa7e5 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -1,4 +1,3 @@ -import sys import app from user_data.settings import * from util.util import curr_directory, remove @@ -11,7 +10,7 @@ __version__ = '0.5.0' def clean(): """Removes all windows libs from libs folder""" - d = curr_directory() + '/libs/' + d = os.path.join(curr_directory(__file__), 'libs') remove(d) @@ -25,10 +24,11 @@ def print_toxygen_version(): def main(): parser = argparse.ArgumentParser() - parser.add_argument('--version', help='Prints Toxygen version') - parser.add_argument('--clean', help='Deletes toxcore libs from libs folder') - parser.add_argument('--reset', help='Resets default profile') - parser.add_argument('profile_path', nargs='?', default=None, help='Resets default profile') + parser.add_argument('--version', action='store_true', help='Prints Toxygen version') + parser.add_argument('--clean', action='store_true', help='Deletes toxcore libs from libs folder') + parser.add_argument('--reset', action='store_true', help='Resets default profile') + parser.add_argument('--uri', help='Adds specified TOX ID to friends') + parser.add_argument('profile', nargs='?', default=None, help='Path to TOX profile') args = parser.parse_args() if args.version: @@ -43,7 +43,7 @@ def main(): reset() return - toxygen = app.App(__version__, path_to_profile=args.profile_path) + toxygen = app.App(__version__, args.profile, args.uri) toxygen.main() diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 4db161e..926c7af 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -1,15 +1,18 @@ -from PyQt5 import QtGui +from PyQt5 import QtGui, QtCore from user_data.settings import Settings from contacts.profile import Profile from wrapper.toxcore_enums_and_consts import * from wrapper.toxav_enums import * from wrapper.tox import bin_to_string -from plugin_support.plugin_support import PluginLoader +import util.ui as util_ui +import util.util as util import cv2 import numpy as np from middleware.threads import invoke_in_main_thread, execute +from notifications.tray import tray_notification +from notifications.sound import * -# TODO: use closures +# TODO: gc callbacks and refactoring # ----------------------------------------------------------------------------------------------------------------- # Callbacks - current user @@ -111,7 +114,8 @@ def friend_message(profile, settings, window, tray): invoke_in_main_thread(tray_notification, friend.name, message, tray, window) if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['MESSAGE']) - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) return wrapped @@ -169,11 +173,12 @@ def tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager if not window.isActiveWindow(): friend = contacts_manager.get_friend_by_number(friend_number) if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY'] and not settings.locked: - file_from = QtWidgets.QApplication.translate("Callback", "File from") + file_from = util_ui.tr("File from") invoke_in_main_thread(tray_notification, file_from + ' ' + friend.name, file_name, tray, window) if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER']) - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) else: # AVATAR print('Avatar') invoke_in_main_thread(file_transfer_handler.incoming_avatar, @@ -240,8 +245,7 @@ def lossy_packet(plugin_loader): Incoming lossy packet """ data = data[:length] - plugin = PluginLoader.get_instance() - invoke_in_main_thread(plugin.callback_lossy, friend_number, data) + invoke_in_main_thread(plugin_loader.callback_lossy, friend_number, data) return wrapped @@ -358,7 +362,8 @@ def show_gc_notification(window, tray, message, group_number, peer_number): invoke_in_main_thread(tray_notification, chat.name + ' ' + peer_name, message, tray, window) if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['MESSAGE']) - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png')) + icon = os.path.join(util.get_images_directory(), 'icon_new_messages.png') + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization @@ -366,17 +371,20 @@ def show_gc_notification(window, tray, message, group_number, peer_number): def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, - calls_manager, file_transfer_handler, window, tray): + calls_manager, file_transfer_handler, main_window, tray): """ Initialization of all callbacks. - :param tox: tox instance - :param window: main window + :param tox: Tox instance + :param profile: Profile instance + :param settings: Settings instance + :param plugin_loader: PluginLoader instance + :param main_window: main window screen :param tray: tray (for notifications) """ tox.callback_self_connection_status(self_connection_status(tox, profile), 0) tox.callback_friend_status(friend_status(profile, settings), 0) - tox.callback_friend_message(friend_message(profile, settings, window, tray), 0) + tox.callback_friend_message(friend_message(profile, settings, main_window, tray), 0) tox.callback_friend_connection_status(friend_connection_status(profile, settings, plugin_loader), 0) tox.callback_friend_name(friend_name(profile), 0) tox.callback_friend_status_message(friend_status_message(profile), 0) @@ -384,11 +392,13 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, tox.callback_friend_typing(friend_typing(contacts_manager), 0) tox.callback_friend_read_receipt(friend_read_receipt(contacts_manager), 0) - tox.callback_file_recv(tox_file_recv(window, tray, profile, file_transfer_handler, contacts_manager, settings), 0) + tox.callback_file_recv(tox_file_recv(main_window, tray, profile, file_transfer_handler, + contacts_manager, settings), 0) tox.callback_file_recv_chunk(file_recv_chunk(file_transfer_handler), 0) tox.callback_file_chunk_request(file_chunk_request(file_transfer_handler), 0) tox.callback_file_recv_control(file_recv_control(file_transfer_handler), 0) + toxav = tox.AV toxav.callback_call_state(call_state(calls_manager), 0) toxav.callback_call(call(calls_manager), 0) toxav.callback_audio_receive_frame(callback_audio(calls_manager), 0) diff --git a/toxygen/middleware/threads.py b/toxygen/middleware/threads.py index eb5a9d7..2d7bdf4 100644 --- a/toxygen/middleware/threads.py +++ b/toxygen/middleware/threads.py @@ -9,44 +9,44 @@ class BaseThread(threading.Thread): def __init__(self): super().__init__() - self._stop = False + self._stop_thread = False def stop_thread(self): - self._stop = True + self._stop_thread = True self.join() class InitThread(BaseThread): - def __init__(self, tox, ms, tray): + def __init__(self, tox, plugin_loader): super().__init__() - self.tox, self.ms, self.tray = tox, ms, tray + self._tox, self._plugin_loader = tox, plugin_loader def run(self): - # initializing callbacks - init_callbacks(self.tox, self.ms, self.tray) # download list of nodes if needed download_nodes_list() + # start plugins + self._plugin_loader.load() # bootstrap try: for data in generate_nodes(): - if self._stop: + if self._stop_thread: return - self.tox.bootstrap(*data) - self.tox.add_tcp_relay(*data) + self._tox.bootstrap(*data) + self._tox.add_tcp_relay(*data) except: pass for _ in range(10): - if self._stop: + if self._stop_thread: return time.sleep(1) - while not self.tox.self_get_connection_status(): + while not self._tox.self_get_connection_status(): try: for data in generate_nodes(): - if self._stop: + if self._stop_thread: return - self.tox.bootstrap(*data) - self.tox.add_tcp_relay(*data) + self._tox.bootstrap(*data) + self._tox.add_tcp_relay(*data) except: pass finally: @@ -60,7 +60,7 @@ class ToxIterateThread(BaseThread): self._tox = tox def run(self): - while not self._stop: + while not self._stop_thread: self._tox.iterate() time.sleep(self._tox.iteration_interval() / 1000) @@ -72,7 +72,7 @@ class ToxAVIterateThread(BaseThread): self._toxav = toxav def run(self): - while not self._stop: + while not self._stop_thread: self._toxav.iterate() time.sleep(self._toxav.iteration_interval() / 1000) @@ -88,7 +88,7 @@ class FileTransfersThread(BaseThread): self._queue.put((func, args, kwargs)) def run(self): - while not self._stop: + while not self._stop_thread: try: func, args, kwargs = self._queue.get(timeout=self._timeout) func(*args, **kwargs) @@ -103,11 +103,11 @@ class FileTransfersThread(BaseThread): _thread = FileTransfersThread() -def start(): +def start_file_transfer_thread(): _thread.start() -def stop(): +def stop_file_transfer_thread(): _thread.stop_thread() diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 9226a81..2831799 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -1,9 +1,7 @@ -from platform import system import json import os -from util.util import log, curr_directory, append_slash +from util.util import log, get_base_directory, append_slash, get_platform import pyaudio -from user_data.toxes import ToxES import smileys.smileys as smileys @@ -60,7 +58,7 @@ class Settings(dict): name = str(auto['name']) if os.path.isfile(append_slash(path) + name + '.tox'): return path, name - return '', '' + return None @staticmethod def set_auto_profile(path, name): @@ -92,9 +90,8 @@ class Settings(dict): fl.write(json.dumps(data)) @staticmethod - def is_active_profile(path, name): - path = path + name + '.lock' - return os.path.isfile(path) + def is_active_profile(profile_path): + return os.path.isfile(profile_path + '.lock') @staticmethod def get_default_settings(): @@ -204,13 +201,14 @@ class Settings(dict): @staticmethod def get_global_settings_path(): - return curr_directory() + '/toxygen.json' + return os.path.join(get_base_directory(), 'toxygen.json') @staticmethod def get_default_path(): - if system() == 'Windows': + system = get_platform() + if system == 'Windows': return os.getenv('APPDATA') + '/Tox/' - elif system() == 'Darwin': + elif system == 'Darwin': return os.getenv('HOME') + '/Library/Application Support/Tox/' else: return os.getenv('HOME') + '/.config/tox/'