def error(self, errStr, cfgPath): """ Error handler GUI implementation """ # it must handle two types of error cases: # - yandex-disk is not installed (errStr=='' in that case) - just show error message and return # - yandex-disk is not configured (errStr!='' in that case) - suggest to configure it and run ya-setup if needed if errStr == '': text1 = _('Yandex.Disk Indicator: daemon start failed') buttons = Gtk.ButtonsType.OK text2 = (_( 'Yandex.Disk utility is not installed.\n' + 'Visit www.yandex.ru, download and install Yandex.Disk daemon.' )) else: text1 = _('Yandex.Disk Indicator: daemon start failed') buttons = Gtk.ButtonsType.OK_CANCEL text2 = (_( 'Yandex.Disk daemon failed to start because it is not' + ' configured properly\n\n' + errStr + '\n\n' + ' To configure it up: press OK button.\n Press Cancel to exit.' )) dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, buttons, text1) dialog.format_secondary_text(text2) dialog.set_icon(APPLOGO) response = dialog.run() if errStr != '' and response == Gtk.ResponseType.OK: # Launch Set-up utility LOGGER.debug('starting configuration utility') retCode = call([pathJoin(APPINSTPATH, 'ya-setup'), cfgPath]) else: retCode = 1 dialog.destroy() return retCode # 0 when error is not critical or fixed (daemon has been configured via ya-setup)
def eventHandler(watch): """ Handles watcher (when watch=False) and and timer (when watch=True) events. After receiving and parsing the daemon output it raises outside change event if daemon changes at least one of its status values. """ # Enter to critical section through acquiring of the lock as it can be called from two different threads self.__lock.acquire() # Parse fresh daemon output. Parsing returns true when something changed if self.__parseOutput(self.__getOutput()): LOGGER.debug('%sEvent raised by %s', self.ID, (' Watcher' if watch else ' Timer')) self.change( self.__v) # Call the callback of update event handler # --- Handle timer delays --- self.__timer.cancel() # Cancel timer if it still active if watch or self.__v['status'] == 'busy': delay = 2 # Initial delay self.__tCnt = 0 # Reset counter else: # It called by timer delay = 2 + self.__tCnt # Increase interval up to 10 sec (2 + 8) self.__tCnt += 1 # Increase counter to increase delay next activation. if self.__tCnt < 9: # Don't start timer after 10 seconds delay self.__timer = thTimer(delay, eventHandler, (False, )) self.__timer.start() # Leave the critical section self.__lock.release()
def appExit(): """ Exit from application (it closes all APPINDICATORS) """ # global APPINDICATORS LOGGER.debug("Exit started") for i in APPINDICATORS: i.exit() Gtk.main_quit()
def update(self, vals, yddir): # Update information in menu self.folder = yddir # Update status data on first run or when status has changed if vals['statchg'] or vals['laststatus'] == 'unknown': self.status.set_label( _('Status: ') + self.YD_STATUS[vals['status']] + (vals['progress'] if vals['status'] == 'busy' else ' '. join((':', vals['error'], shortPath(vals['path']) )) if vals['status'] == 'error' else '')) # Update pseudo-static items on first run or when daemon has stopped or started if 'none' in (vals['status'], vals['laststatus'] ) or vals['laststatus'] == 'unknown': started = vals['status'] != 'none' self.status.set_sensitive(started) # zero-space UTF symbols are used to detect requered action without need to compare translated strings self.daemon_ss.set_label(( '\u2060' + _('Stop Yandex.Disk daemon')) if started else ( '\u200B' + _('Start Yandex.Disk daemon'))) if self.ID != '': # Set daemon identity row in multidaemon mode self.yddir.set_label(self.ID + _(' Folder: ') + (shortPath(yddir) if yddir else '< NOT CONFIGURED >')) self.open_folder.set_sensitive( yddir != '') # Activate Open YDfolder if daemon configured # Update sizes data on first run or when size data has changed if vals['szchg'] or vals['laststatus'] == 'unknown': self.used.set_label( _('Used: ') + vals['used'] + '/' + vals['total']) self.free.set_label( _('Free: ') + vals['free'] + _(', trash: ') + vals['trash']) # Update last synchronized sub-menu on first run or when last data has changed if vals['lastchg'] or vals['laststatus'] == 'unknown': # Update last synchronized sub-menu self.lastItems.destroy( ) # Disable showing synchronized sub menu while updating it - temp fix for #197 self.lastItems = Gtk.Menu() # Create new/empty Sub-menu: for filePath in vals['lastitems']: # Create new sub-menu items # Create menu label as file path (shorten it down to 50 symbols when path length > 50 # symbols), with replaced underscore (to disable menu acceleration feature of GTK menu). widget = Gtk.MenuItem.new_with_label(shortPath(filePath)) filePath = pathJoin(yddir, filePath) # Make full path to file if pathExists(filePath): widget.set_sensitive( True) # If it exists then it can be opened widget.connect("activate", self.openPath, filePath) else: widget.set_sensitive( False) # Don't allow to open non-existing path self.lastItems.append(widget) self.last.set_submenu(self.lastItems) # Switch off last items menu sensitivity if no items in list self.last.set_sensitive(vals['lastitems']) LOGGER.debug("Sub-menu 'Last synchronized' has %s items", str(len(vals['lastitems']))) self.show_all() # Renew menu
def exit(self): # Handle daemon/indicator closing LOGGER.debug("Indicator %sexit started: ", self.ID) self.__watcher.stop() self.__timer.cancel() # stop event timer if it is running # Stop yandex-disk daemon if it is required by its configuration if self.config.get('stoponexitfromindicator', False): self.stop(wait=True) LOGGER.info('Demon %sstopped', self.ID) LOGGER.debug('Indicator %sexited', self.ID)
def send(self, messg): # global APPLOGO LOGGER.debug('Message: %s | %s', self.title, messg) if self.note is not None: try: self.note.close() except: pass self.note = None try: # Create notification self.note = Notify.Notification.new(self.title, messg) self.note.set_image_from_pixbuf(APPLOGO) self.note.show() # Display new notification except: LOGGER.error('Message engine failure')
def onButtonToggled(self, _, button, key, dconfig=None, ow=None): """ Handle clicks on controls """ toggleState = button.get_active() LOGGER.debug('Togged: %s val: %s', key, str(toggleState)) # Update configurations if key in [ 'read-only', 'overwrite', 'startonstartofindicator', 'stoponexitfromindicator' ]: dconfig[key] = toggleState # Update daemon config dconfig.changed = True else: APPCONF.changed = True # Update application config APPCONF[key] = toggleState if key == 'theme': for i in APPINDICATORS: # Update all APPINDICATORS' icons i.setIconTheme(toggleState) # Update icon theme i.updateIcon(i.currentStatus) # Update current icon elif key == 'autostart': if toggleState: copyFile(APPAUTOSTARTSRC, APPAUTOSTARTDST) else: deleteFile(APPAUTOSTARTDST) elif key == 'fmextensions': if not button.get_inconsistent(): # It is a first call if not activateActions(toggleState, APPINSTPATH): toggleState = not toggleState # When activation/deactivation is not success: revert settings back button.set_inconsistent( True) # set inconsistent state to detect second call button.set_active( toggleState) # set check-button to reverted status # set_active will raise again the 'toggled' event else: # This is a second call button.set_inconsistent( False) # Just remove inconsistent status elif key == 'read-only': ow.set_sensitive(toggleState)
def change(self, vals): """ Updates handler """ LOGGER.debug('%sUpdate event: %s', self.ID, str(vals))
def error(self, errStr, cfgPath): """ Error handler """ LOGGER.debug('%sError %s , path %s', self.ID, errStr, cfgPath) return 0
# Change the process name setProcName(APPHOME) # Check for already running instance of the indicator application # Get PIDs of all runnig processes (of current user) with name 'yd-tools' and compare it with current process PID if str(getpid()) != check_output( ["pgrep", '-u', str(geteuid()), APPHOME], universal_newlines=True).strip(): sysExit(_('The indicator instance is already running.')) # Set user specified logging level LOGGER.setLevel(args.level) # Report app version and logging level LOGGER.info('%s v.%s', APPNAME, APPVER) LOGGER.debug('Logging level: %s', str(args.level)) # Application configuration """ User configuration is stored in ~/.config/<APPHOME>/<APPNAME>.conf file. This file can contain comments (line starts with '#') and config values in form: key=value[,value[,value ...]] where keys and values can be quoted ("...") or not. The following key words are reserved for configuration: autostart, notifications, theme, fmextensions and daemons. The dictionary 'config' stores the config settings for usage in code. Its values are saved to config file on exit from the Menu.Preferences dialogue or when there is no configuration file when application starts. Note that daemon settings ('dir', 'read-only', 'overwrite' and 'exclude_dir') are stored in ~/ .config/yandex-disk/config.cfg file. They are read in YDDaemon.__init__() method