diff --git a/app/hub.py b/app/hub.py index fb54f6a..fa06016 100644 --- a/app/hub.py +++ b/app/hub.py @@ -678,6 +678,23 @@ def update_wallet_loading_height(self): if h > self.current_block_height: self.on_update_wallet_loading_height_event.emit(h, self.ui.target_height) self.current_block_height = h + + @Slot(bool) + def change_minimize_to_tray(self, status): + self.ui.close_to_system_tray = status + self.ui.app_settings.settings['application']['minimize_to_tray'] = status + self.ui.app_settings.save() + + @Slot(int) + def change_limit_rate_up(self, limit_rate_up): + self.ui.app_settings.settings['daemon']['limit_rate_up'] = limit_rate_up + self.ui.app_settings.save() + + + @Slot(int) + def change_limit_rate_down(self, limit_rate_down): + self.ui.app_settings.settings['daemon']['limit_rate_down'] = limit_rate_down + self.ui.app_settings.save() def update_daemon_status(self, status): diff --git a/classes/__init__.py b/classes/__init__.py index 443f493..1c7ad80 100644 --- a/classes/__init__.py +++ b/classes/__init__.py @@ -81,18 +81,36 @@ def reset(self): self.bc_height = -1 +def set_default_settings(default_settings, settings): + for k, v in default_settings.iteritems(): + settings.setdefault(k, v) + if type(v) is dict: set_default_settings(default_settings[k], settings[k]) + class AppSettings(): - settings = { + settings = {} + default_settings = { "daemon": { "log_level": 0, - "block_sync_size": 10 + "block_sync_size": 10, + "limit_rate_up": 2048, + "limit_rate_down": 8192, }, "blockchain": { "height": 0, + }, + + "application": { + "minimize_to_tray": False, } } + log_levels = [0,1,2,3,4] + block_sync_sizes = [10,20,50,100,200] + limit_rate_ups = [512,1024,2048,3072,4096,8192,16384] + limit_rate_downs = [512,1024,2048,4096,8192,12288,16384] + + def __init__(self): self.app_settings_filepath = os.path.join(config_path, 'app_settings.json') @@ -101,12 +119,36 @@ def load(self): if os.path.exists(self.app_settings_filepath): try: self.settings = json.loads(readFile(self.app_settings_filepath)) - return True except Exception, err: - log("[AppSettings]>>> Load error:" + str(err), LEVEL_ERROR) - return False - return False - + log("[AppSettings]>>> Load config file error:" + str(err), LEVEL_ERROR) + + # Set default values: + set_default_settings(self.default_settings, self.settings) + + # Validate values + if self.settings["daemon"]["log_level"] not in self.log_levels: + self.settings["daemon"]["log_level"] = self.default_settings["daemon"]["log_level"] + + if self.settings["daemon"]["block_sync_size"] not in self.block_sync_sizes: + self.settings["daemon"]["block_sync_size"] = self.default_settings["daemon"]["block_sync_size"] + + if self.settings["daemon"]["limit_rate_up"] not in self.limit_rate_ups: + self.settings["daemon"]["limit_rate_up"] = self.default_settings["daemon"]["limit_rate_up"] + + if self.settings["daemon"]["limit_rate_down"] not in self.limit_rate_downs: + self.settings["daemon"]["limit_rate_down"] = self.default_settings["daemon"]["limit_rate_down"] + + try: + self.settings["blockchain"]["height"] = abs(int(self.settings["blockchain"]["height"])) + except: + self.settings["blockchain"]["height"] = self.default_settings["blockchain"]["height"] + + try: + self.settings["application"]["minimize_to_tray"] = bool(self.settings["application"]["minimize_to_tray"]) + except: + self.settings["application"]["minimize_to_tray"] = self.default_settings["application"]["minimize_to_tray"] + + def save(self): try: writeFile(self.app_settings_filepath, \ diff --git a/html/index.py b/html/index.py index b9ec9c4..40cff45 100644 --- a/html/index.py +++ b/html/index.py @@ -89,7 +89,7 @@ #settings_tab h3{ margin-top: 20px; - margin-bottom: 30px; + margin-bottom: 20px; } .syncing{ @@ -374,6 +374,10 @@ text-align: center; } + #speed_limit_form select { + font-size: 14px; + } + @@ -391,6 +395,11 @@ $('#daemon_log_level_' + log_level).prop('checked', true); var block_sync_size = app_settings['daemon']['block_sync_size']; $('#block_sync_size_' + block_sync_size).prop('checked', true); + + $('#minimize_to_tray_chk').prop('checked', app_settings['application']['minimize_to_tray']); + + $('#up_speed_limit_select').val(app_settings['daemon']['limit_rate_up']); + $('#down_speed_limit_select').val(app_settings['daemon']['limit_rate_down']); }); app_hub.on_main_wallet_ui_reset_event.connect(function(){ @@ -754,7 +763,7 @@ var row_rendered = Mustache.render(new_subaddress_row_tmpl, { 'address_index': subaddress['address_index'], 'address' : subaddress['address'], - 'address_short' : subaddress['address'].substr(0, 70) + '...' + 'address_short' : subaddress['address'].substr(0, 60) + '...' }); @@ -772,7 +781,7 @@ var row_rendered = Mustache.render(used_subaddress_row_tmpl, { 'address_index': subaddress['address_index'], 'address' : subaddress['address'], - 'address_short' : subaddress['address'].substr(0, 40) + '...', + 'address_short' : subaddress['address'].substr(0, 30) + '...', 'balance': subaddress['balance'], 'unlocked_balance': subaddress['unlocked_balance'], 'row_font_weight': subaddress['address_index'] == 0 ? 'bold' : 'normal' @@ -1086,6 +1095,18 @@ }, 1); } }); + + $("#minimize_to_tray_chk").change(function() { + app_hub.change_minimize_to_tray(this.checked); + }); + + $('#up_speed_limit_select').on('change', function(e) { + app_hub.change_limit_rate_up(this.value); + }); + + $('#down_speed_limit_select').on('change', function(e) { + app_hub.change_limit_rate_down(this.value); + }); }); @@ -1326,15 +1347,15 @@ -
-

DAEMON

+
+

DAEMON

-
+
- -
+ +
-
+
- -
+ +
-
- +
+
+
+
+ + +
+
+ + +
+
+
+
+
+
-
+
+
+
+ +
+
diff --git a/manager/ProcessManager.py b/manager/ProcessManager.py index 1f4b2a5..fddfded 100644 --- a/manager/ProcessManager.py +++ b/manager/ProcessManager.py @@ -49,34 +49,38 @@ def send_command(self, cmd): def stop(self): if self.is_proc_running(): - self.send_command('exit') - #self.proc.stdin.close() - counter = 0 - while True: - if self.is_proc_running(): - if counter < 10: - if counter == 2: - try: - self.send_command('exit') - except: - pass - sleep(1) - counter += 1 + try: + self.send_command('exit') + #self.proc.stdin.close() + counter = 0 + while True: + if self.is_proc_running(): + if counter < 10: + if counter == 2: + try: + self.send_command('exit') + except: + pass + sleep(1) + counter += 1 + else: + self.proc.kill() + log("[%s] killed" % self.proc_name, LEVEL_INFO, self.proc_name) + break else: - self.proc.kill() - log("[%s] killed" % self.proc_name, LEVEL_INFO, self.proc_name) break - else: - break - log("[%s] stopped" % self.proc_name, LEVEL_INFO, self.proc_name) + log("[%s] stopped" % self.proc_name, LEVEL_INFO, self.proc_name) + except IOError: + pass + def is_proc_running(self): return (self.proc.poll() is None) class SumokoindManager(ProcessManager): - def __init__(self, resources_path, log_level=0, block_sync_size=10): - proc_args = u'%s/bin/sumokoind --log-level %d --block-sync-size %d' % (resources_path, log_level, block_sync_size) + def __init__(self, resources_path, log_level=0, block_sync_size=10, limit_rate_up=2048, limit_rate_down=8192): + proc_args = u'%s/bin/sumokoind --log-level %d --block-sync-size %d --limit-rate-up %d --limit-rate-down %d' % (resources_path, log_level, block_sync_size, limit_rate_up, limit_rate_down) ProcessManager.__init__(self, proc_args, "sumokoind") self.synced = Event() self.stopped = Event() @@ -110,7 +114,7 @@ def __init__(self, resources_path, wallet_file_path, wallet_log_path, restore_wa wallet_args = u'%s/bin/sumo-wallet-cli --log-file=%s --restore-deterministic-wallet --restore-height %d' \ % (resources_path, wallet_log_path, restore_height) ProcessManager.__init__(self, wallet_args, "sumo-wallet-cli") - self.ready = Event() + self._ready = Event() self.last_error = "" self.block_height = 0 self.top_height = 0 @@ -125,11 +129,12 @@ def run(self): self.block_height = int(m_height.group(1)) self.top_height = int(m_height.group(2)) - if not self.ready.is_set() and is_ready_str in line: - self.ready.set() + if not self._ready.is_set() and is_ready_str in line: + self._ready.set() log("[%s]>>> %s" % (self.proc_name, line.rstrip()), LEVEL_INFO, self.proc_name) log("[%s]>>> %s" % (self.proc_name, "Wallet ready!") , LEVEL_INFO, self.proc_name) - elif err_str in line: + + if err_str in line: self.last_error = line.rstrip() log("[%s]>>> %s" % (self.proc_name, line.rstrip()), LEVEL_ERROR, self.proc_name) elif m_height: @@ -141,7 +146,7 @@ def run(self): self.proc.stdout.close() def is_ready(self): - return self.ready.is_set() + return self._ready.is_set() def is_connected(self): @@ -165,15 +170,15 @@ def __init__(self, resources_path, wallet_file_path, wallet_password, app, log_l self.rpc_request = WalletRPCRequest(app, self.user_agent) # self.rpc_request.start() - self._ready = False + self._ready = Event() self.block_height = 0 self.is_password_invalid = Event() self.last_log_lines = [] def run(self): - is_ready_str = "Starting wallet rpc server" + rpc_ready_strs = ["Binding on 127.0.0.1", "Starting wallet rpc server", "Run net_service loop", "Refresh done"] err_str = "ERROR" - invalid_password = "invalid password" + invalid_password_str = "invalid password" height_regex = re.compile(r"Processed block: \<([a-z0-9]+)\>, height (\d+)") height_regex2 = re.compile(r"Skipped block by height: (\d+)") height_regex3 = re.compile(r"Skipped block by timestamp, height: (\d+)") @@ -188,13 +193,14 @@ def run(self): m_height = height_regex3.search(line) if m_height: self.block_height = m_height.group(1) - if not self._ready and is_ready_str in line: - self._ready = True - log(line.rstrip(), LEVEL_INFO, self.proc_name) - elif err_str in line: + if not self._ready.is_set() and any(s in line for s in rpc_ready_strs): + self._ready.set() + log("RPC server ready!", LEVEL_INFO, self.proc_name) + + if err_str in line: self.last_error = line.rstrip() log(line.rstrip(), LEVEL_ERROR, self.proc_name) - if not self.is_password_invalid.is_set() and invalid_password in self.last_error: + if not self.is_password_invalid.is_set() and invalid_password_str in self.last_error: self.is_password_invalid.set() elif m_height: log(line.rstrip(), LEVEL_INFO, self.proc_name) @@ -210,7 +216,7 @@ def run(self): self.proc.stdout.close() def is_ready(self): - return self._ready + return self._ready.is_set() def is_invalid_password(self): return self.is_password_invalid.is_set() diff --git a/rpc/__init__.py b/rpc/__init__.py index 36c2f36..e1f3eab 100644 --- a/rpc/__init__.py +++ b/rpc/__init__.py @@ -160,7 +160,7 @@ def get_balance(self): if 'per_subaddress' in res: per_subaddress = res['per_subaddress'] return (res['balance'], res['unlocked_balance'], per_subaddress) - return (0, 0) + return (0, 0, []) def get_transfers(self, filter_by_height=False, min_height=0, max_height=0, tx_in=True, tx_out=True, tx_pending=False, tx_in_pool=False): rpc_input = {"method":"get_transfers"} diff --git a/settings/__init__.py b/settings/__init__.py index df87da7..067445b 100644 --- a/settings/__init__.py +++ b/settings/__init__.py @@ -15,7 +15,7 @@ USER_AGENT = "Sumokoin GUI Wallet" APP_NAME = "Sumokoin GUI Wallet" -VERSION = [0, 0, 5] +VERSION = [0, 0, 6] _data_dir = makeDir(os.path.join(getHomeDir(), 'SumokoinGUIWallet')) diff --git a/webui/__init__.py b/webui/__init__.py index 26b0e0f..15f8f32 100644 --- a/webui/__init__.py +++ b/webui/__init__.py @@ -247,10 +247,21 @@ def __init__(self, app, hub, debug): self.notifier = Notify(APP_NAME) self.trayIcon.show() + self.close_to_system_tray = self.app_settings.settings['application']['minimize_to_tray'] def closeEvent(self, event): """ Override QT close event """ + if not self.close_to_system_tray: + reply=QMessageBox.question(self,'Exit %s?' % APP_NAME, + "Are you sure to exit %s?" % APP_NAME, \ + QMessageBox.Yes | QMessageBox.No, defaultButton=QMessageBox.Yes) + if reply == QMessageBox.Yes: + self.hide_wallet() + else: + event.ignore() + return + event.ignore() self.hide() if not self.system_tray_running_notified: @@ -278,7 +289,9 @@ def start_deamon(self): #start sumokoind daemon self.sumokoind_daemon_manager = SumokoindManager(self.app.property("ResPath"), self.app_settings.settings['daemon']['log_level'], - self.app_settings.settings['daemon']['block_sync_size']) + self.app_settings.settings['daemon']['block_sync_size'], + self.app_settings.settings['daemon']['limit_rate_up'], + self.app_settings.settings['daemon']['limit_rate_down']) self.sumokoind_daemon_manager.start()