def context_menu(self, pos, image_hash=None): """Show custom context menu""" menu = self.page.createStandardContextMenu() menu.clear() menu.addAction(self.page.action(QWebPage.Cut)) menu.addAction(self.page.action(QWebPage.Copy)) menu.addAction(self.page.action(QWebPage.Paste)) paste_wo = self.page.action(QWebPage.PasteAndMatchStyle) paste_wo.setText(self.tr('Paste as Plain Text')) menu.addAction(paste_wo) if self._hovered_url: menu.addAction(self.page.action(QWebPage.CopyLinkToClipboard)) menu.addAction(self.change_link) menu.addAction(self.remove_link) self.page.active_link = None if self.page.active_image: res = self.parent.resource_edit.get_by_hash(self.page.active_image) self.page.active_image = None menu.addAction( self.tr('Image Preferences'), Slot()(partial(self._show_image_dialog, res)), ) if self.page.active_table: menu.addAction( self.tr('Change table'), Slot()(partial(self._update_table, self.page.active_table)), ) self.page.active_table = None menu.addSeparator() menu.addAction(self.page.action(QWebPage.RemoveFormat)) menu.addAction(self.page.action(QWebPage.SelectAll)) menu.exec_(self.widget.mapToGlobal(pos))
def click(self, res, event): """Open resource""" button = event.button() if button == Qt.LeftButton: subprocess.Popen(['xdg-open', res.file_path]) elif button == Qt.RightButton: menu = QMenu(self.parent) menu.addAction( self.app.translate('ResourceEdit', 'Put to Content'), Slot()(partial( self.to_content, res=res, )), ) if not self.parent.note_edit.in_content(res): menu.addAction( self.app.translate('ResourceEdit', 'Remove Resource'), Slot()(partial( self.remove, res=res, ))) menu.addAction(self.app.translate('ResourceEdit', 'Save As'), Slot()(partial( self.save, res=res, ))) menu.exec_(event.globalPos())
def __init__(self, parent, widget, on_change): """Init and connect signals""" self.parent = parent self.app = QApplication.instance() self.widget = widget for notebook_struct in self.app.provider.list_notebooks(): notebook = Notebook.from_tuple(notebook_struct) self.widget.addItem(notebook.name, userData=notebook.id) self.widget.currentIndexChanged.connect(Slot()(on_change))
def __init__(self, verbose, *args, **kwargs): App.__init__(self, *args, **kwargs) self.settings = QSettings('everpad', 'everpad-provider') self.verbose = verbose session_bus = dbus.SessionBus() self.bus = dbus.service.BusName("com.everpad.Provider", session_bus) self.service = ProviderService(self, session_bus, '/EverpadProvider') self.sync_thread = SyncThread(self) self.sync_thread.sync_state_changed.connect( Slot(int)(self.service.sync_state_changed), ) self.sync_thread.data_changed.connect( Slot()(self.service.data_changed), ) if get_auth_token(): self.sync_thread.start() self.service.qobject.authenticate_signal.connect( self.on_authenticated, ) self.service.qobject.remove_authenticate_signal.connect( self.on_remove_authenticated, )
def _init_actions(self): self.check_action = QAction(self.tr('Insert Checkbox'), self) self.check_action.triggered.connect(self._insert_check) self.link_action = QAction(self.tr('Insert Link'), self) self.link_action.triggered.connect(self._insert_link) self.table_action = QAction(self.tr('Insert Table'), self) self.table_action.triggered.connect(self._insert_table) self.image_action = QAction(self.tr('Insert Image'), self) self.image_action.triggered.connect(self._insert_image) self.change_link = QAction(self.tr('Change link'), self) self.change_link.triggered.connect(Slot()(partial( self._change_link, self.page.active_link))) self.remove_link = QAction(self.tr('Remove link'), self) self.remove_link.triggered.connect(self._remove_link)
def _init_shortcuts(self): for key, action in ( ('Ctrl+b', QWebPage.ToggleBold), ('Ctrl+i', QWebPage.ToggleItalic), ('Ctrl+u', QWebPage.ToggleUnderline), ('Ctrl+Shift+b', QWebPage.InsertUnorderedList), ('Ctrl+Shift+o', QWebPage.InsertOrderedList), ('Ctrl+shift+v', QWebPage.PasteAndMatchStyle), ): QShortcut( QKeySequence(self.app.tr(key)), self.widget, ).activated.connect( Slot()(partial(self._action_for_key, action)), )
def __init__(self, parent, widget, on_change): """Init and connect signals""" self.parent = parent self.app = QApplication.instance() self.widget = widget self.tags_list = map( lambda tag: Tag.from_tuple(tag).name, self.app.provider.list_tags(), ) self.completer = QCompleter() self.completer_model = QStringListModel() self.completer.setModel(self.completer_model) self.completer.activated.connect(self.update_completion) self.update_completion() self.widget.setCompleter(self.completer) self.widget.textChanged.connect(Slot()(on_change)) self.widget.textEdited.connect(self.update_completion)
def _init_shortcuts(self): for key, action in ( ('Ctrl+b', QWebPage.ToggleBold), ('Ctrl+i', QWebPage.ToggleItalic), ('Ctrl+u', QWebPage.ToggleUnderline), ('Ctrl+Shift+b', QWebPage.InsertUnorderedList), ('Ctrl+Shift+o', QWebPage.InsertOrderedList), ('Ctrl+Shift+v', QWebPage.PasteAndMatchStyle), ('Ctrl+k', self.link_action), ('Ctrl+Shift+k', self.change_link), ('Ctrl+l', QWebPage.AlignLeft), ('Ctrl+r', QWebPage.AlignRight), ('Ctrl+e', QWebPage.AlignCenter), ('Ctrl+j', QWebPage.AlignJustified), ('Ctrl+t', QWebPage.ToggleStrikethrough), ('Ctrl+Space', QWebPage.RemoveFormat), ('Ctrl+Shift+c', self.check_action), ): QShortcut( QKeySequence(self.app.tr(key)), self.widget, ).activated.connect(Slot()(partial(self._action_for_key, action)), )
def __init__(self, verbose, *args, **kwargs): # non-kde: # from PySide.QtCore import QCoreApplication # AppClass = QCoreApplication AppClass.__init__(self, *args, **kwargs) # ************************************************************ # Configure logger # ************************************************************ # https://docs.python.org/2/library/logging.html # good ref: # http://victorlin.me/posts/2012/08/26/good-logging-practice-in-python # Yes, quite drawn out with all my if verbose, but readable for me when # I come back to this in a couple weeks or more #logging.basicConfig(level=logging.INFO) # create logger and set to debug self.logger = logging.getLogger('gevernote-provider') self.logger.setLevel(logging.DEBUG) fh = logging.FileHandler( os.path.expanduser('~/.everpad/logs/gevernote-provider.log')) fh.setLevel(logging.DEBUG) fh.setFormatter( logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s')) self.logger.addHandler(fh) if verbose: ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) ch.setFormatter(logging.Formatter('%(asctime)s - %(message)s')) self.logger.addHandler(ch) self.logger.info('Logging started.') # ref: http://qt-project.org/doc/qt-4.8/qsettings.html # # For example, if your product is called Star Runner and your company # is called MySoft, you would construct the QSettings object as follows: # QSettings settings("MySoft", "Star Runner"); # Backwards? self.settings = QSettings('everpad', 'everpad-provider') # going to do more here - gsettings self.logger.debug('Setting parsed.') # Ref: http://excid3.com/blog/an-actually-decent-python-dbus-tutorial/ # SessionBus because service is a session level daemon session_bus = dbus.SessionBus() # for future name change #self.bus = dbus.service.BusName("com.gevernote.Provider", session_bus) #self.service = ProviderService(session_bus, '/GrevernoteProvider') self.bus = dbus.service.BusName("com.everpad.Provider", session_bus) self.service = ProviderService(session_bus, '/EverpadProvider') self.logger.debug("dbus setup complete") # subclass PySide.QtCore.QThread - agent.py # setup Sync thread self.sync_thread = SyncThread() # connect Sync thread sync_state_changed self.sync_thread.sync_state_changed.connect( Slot(int)(self.service.sync_state_changed), ) # connect Sync thread data_changed self.sync_thread.data_changed.connect( Slot()(self.service.data_changed), ) self.logger.debug("SyncThread init complete") # Start Sync Thread if provider is authenticated if get_auth_token(): self.logger.debug('Auth - Starting Sync Thread.') self.sync_thread.start() else: self.logger.debug('No Auth - Sync Thread not started.') # ************************************************************ # Authentication and Termination Signals Setup # ************************************************************ # provider_authenticate @Slot self.service.qobject.authenticate_signal.connect( self.provider_authenticate, ) # on_authenticated @Slot #self.service.qobject.authenticate_signal.connect( # self.on_authenticated, #) # on_remove_authenticated @Slot self.service.qobject.remove_authenticate_signal.connect( self.on_remove_authenticated, ) self.service.qobject.terminate.connect(self.terminate) self.logger.info('Provider started.')
def update(self): self.menu.clear() try: version = self.app.provider.get_api_version() except ( # dbus raise some magic dbus.exceptions.UnknownMethodException, dbus.exceptions.DBusException, ): version = -1 if version != API_VERSION: action = self.menu.addAction( self.tr('API version missmatch, please restart'), ) action.setEnabled(False) if version < API_VERSION: handler = self.app.provider.kill else: handler = partial(os.execlp, 'everpad', '--replace') self.menu.addAction( self.tr('Restart everpad'), handler, ) return if self.app.provider.is_authenticated(): pin_notes = self.app.provider.find_notes( '', dbus.Array([], signature='i'), dbus.Array([], signature='i'), 0, 20, Note.ORDER_UPDATED_DESC, 1, ) sort_by_notebook = bool( int( self.app.provider.get_settings_value('sort-by-notebook') or 0)) has_notes = False if not sort_by_notebook: notes = self.app.provider.find_notes( '', dbus.Array([], signature='i'), dbus.Array([], signature='i'), 0, 20 - len(pin_notes), Note.ORDER_UPDATED_DESC, 0, ) has_notes = bool(notes) else: notebooks = self.app.provider.list_notebooks() notes = {} for notebook_struct in notebooks: notebook = Notebook.from_tuple(notebook_struct) _notes = self.app.provider.find_notes( '', [notebook.id], dbus.Array([], signature='i'), 0, 20 - len(pin_notes), Note.ORDER_UPDATED_DESC, 0, ) notes[notebook] = _notes if _notes: has_notes = True first_sync = not (has_notes or len(pin_notes) or self.app.provider.is_first_synced()) # Rate Limit indication added # STATUS_RATE = -1 # Rate Limit status # STATUS_NONE = 0 # STATUS_SYNC = 1 # status_syncing = self.app.provider.get_status() == STATUS_SYNC status_syncing = self.app.provider.get_status() if status_syncing < 0: sync_label = self.tr('Rate Limit') elif status_syncing and first_sync: sync_label = self.tr('Wait, first sync in progress') elif status_syncing and not first_sync: sync_label = self.tr('Sync in progress') elif not status_syncing and first_sync: sync_label = self.tr('Please perform first sync') else: last_sync = self.app.provider.get_last_sync() delta_sync = (datetime.now() - datetime.strptime( last_sync, '%H:%M')).seconds // 60 if delta_sync == 0: sync_label = self.tr('Last Sync: Just now') elif delta_sync == 1: sync_label = self.tr('Last Sync: %s min ago') % delta_sync else: sync_label = self.tr('Last Sync: %s mins ago') % delta_sync menu_items = { 'create_note': [self.tr('Create Note'), self.create], 'all_notes': [self.tr('All Notes'), self.show_all_notes], 'sync': [sync_label, Slot()(self.app.provider.sync)], 'pin_notes': pin_notes, 'notes': notes, } for item in self.app.settings.value('menu-order', DEFAULT_INDICATOR_LAYOUT): if item == 'pin_notes' or item == 'notes': if not first_sync and len(menu_items[item]): self.menu.addSeparator() if item == 'notes' and sort_by_notebook: for notebook in menu_items[item]: sub_menu = self.menu.addMenu(notebook.name) _notes = menu_items[item][notebook] for struct in _notes: self._add_note(sub_menu, struct) else: for struct in menu_items[item]: self._add_note(self.menu, struct) self.menu.addSeparator() else: action = self.menu.addAction(menu_items[item][0], menu_items[item][1]) if status_syncing and item == 'sync': action.setEnabled(False) self.menu.addSeparator() self.menu.addAction(self.tr('Settings and Management'), self.show_management) self.menu.addAction(self.tr('Exit'), self.exit)
def _add_note(self, menu, struct): note = Note.from_tuple(struct) title = note.title[:40].replace('&', '&&') menu.addAction(title, Slot()(partial(self.open, note=note)))
# Add/update to scene dict self.attribute_scenes_dict[scene.attributes] = scene # Determine how to handle this if self.apply_attribute_scenes: changes = False for request in self.requests.values(): changes = request.receiveAttributeScene(scene) or changes if changes: # If any of these caused a change, alert the module self.attributeSceneUpdateSignal.emit() # The slots need to be added down here because ModuleAgent is not defined # at the time that the functions are defined ModuleAgent.addChildCoupler = Slot(FilterCoupler, ModuleAgent)(ModuleAgent.addChildCoupler) ModuleAgent.receiveSceneFromChild = Slot(Scene, ModuleAgent)(ModuleAgent.receiveSceneFromChild) ModuleAgent.sendAllScenes = Slot(ModuleAgent)(ModuleAgent.sendAllScenes) class ModuleRequest(QObject): """Holds all of the requested information including the desired attributes and the operation to perform on them. This is identified by the name member of the class. Please keep names unique within a single module as they are used to differentiate requests. """ operator = { 'sum' : sum, 'mean' : lambda x: sum(x) / float(len(x)),
class Fourier(QWidget): """ Actual fourier display and drawing widget """ fourier_updated = Signal(np.ndarray) image = None pressed = False scale_factor = None color = QColor(0, 0, 0) raw_fourier = None x_sym = False y_sym = False opp_sym = False # signal fourier updated def __init__(self, parent=None): super().__init__(parent) self.update_cursor('square', 20) self.overlay = Overlay(self) def update_image(self, fft_array, factor=None): scaled_values, sf = rescale_array(np.real(np.log2(fft_array)), self.scale_factor) self.scale_factor = sf self.image = array_to_image(scaled_values) self.setMinimumSize(self.image.size()) self.overlay.resize(self.image.size()) self.update() self.updateGeometry() # I have to deliver, and it's broken on windows. self.setMinimumSize(self.image.size()) self.overlay.resize(self.image.size()) self.update() self.updateGeometry() def update_fourier(self, image_array, flush=False): f = array_to_fft(image_array) if self.raw_fourier is None or flush: self.original = f.copy() self.raw_fourier = f.copy() self.update_image(self.raw_fourier) def update_cursor(self, shape, size): self.cursor_size = size self.shape = shape cursor_pix = QPixmap(size, size) cursor_pix.fill(Qt.transparent) painter = QPainter(cursor_pix) painter.setPen(QColor(255, 0, 0)) if shape == 'circle': painter.drawEllipse(0, 0, size - 1, size - 1) elif shape == 'square': painter.drawRect(0, 0, size - 1, size - 1) elif shape == "magic wand": magic_wand.render(painter, QRect(0, 0, 20, 20)) cursor = QCursor(cursor_pix, 0, 0) self.setCursor(cursor) del painter def on_size_change(self, size): """ Brush size changed """ self.cursor_size = size self.update_cursor(self.shape, self.cursor_size) Slot(str) def on_shape_change(self, shape): """ Brush shape changed """ self.shape = shape self.update_cursor(self.shape, self.cursor_size) def on_color_change(self, color): self.color = QColor(color, color, color) def emit_fourier(self): array = fft_to_array(self.raw_fourier) self.fourier_updated.emit(array) def on_restore(self): self.raw_fourier = self.original.copy() self.update_image(self.raw_fourier, self.scale_factor) #self.fourier_updated.emit(self.raw_fourier.copy()) self.emit_fourier() def on_resize(self, factor): if factor == 1.0: return #even = lambda x: x if (x % 2 == 0) else x + 1 array = self.raw_fourier reshape = lambda x_y: [int(factor * x_y[0]), int(factor * x_y[1])] diff = lambda x_y: [x_y[0] - array.shape[0], x_y[1] - array.shape[1]] nexteven = lambda x: x if (x % 2 == 0) else x + 1 delta = map(nexteven, diff(reshape(array.shape))) newsize = tuple(x[0] + x[1] for x in zip(array.shape, delta)) self.raw_fourier = zeropad(array, newsize) self.update_image(self.raw_fourier, self.scale_factor) #self.fourier_updated.emit(self.raw_fourier.copy()) self.emit_fourier() def draw_mask_on_fourier(self, mask, value=0x00): self.raw_fourier[mask] = 2**value self.update_image(self.raw_fourier, self.scale_factor) self.emit_fourier() def regen_image(self): self.update_image(self.raw_fourier, self.scale_factor) def paintEvent(self, event): """ Paint widget as self.image content """ if self.image is None: super().paintEvent(event) return painter = QPainter(self) rect = event.rect() painter.drawImage(rect.topLeft(), self.image, rect) def mousePressEvent(self, event): self.pressed = True self.draw_buffer = QPixmap(self.image.size()) color = self.color.red() ^ 0xAA self.draw_buffer.fill(QColor(color, color, color)) self.draw(event) def mouseReleaseEvent(self, event): self.pressed = False self.draw(event) self.fourier_draw() def mouseMoveEvent(self, event): if self.pressed: self.draw(event) def fourier_draw(self): arr = image_to_array(self.draw_buffer.toImage()) values = arr[arr == self.color.red()] * (self.scale_factor / 255) self.raw_fourier[arr == self.color.red()] = np.abs(2**values) self.emit_fourier() def _paint(self, painter, x, y): size = self.cursor_size shape = self.shape painter.setBrush(self.color) painter.setPen(Qt.NoPen) if shape == 'circle': painter.drawEllipse(x, y, size - 1, size - 1) elif shape == 'square': painter.drawRect(x, y, size - 1, size - 1) elif shape == "magic wand": magic_wand.render(painter, QRect(0, 0, 20, 20)) def draw(self, event): x, y = event.x(), event.y() max_y, max_x = self.raw_fourier.shape for painter in map(QPainter, [self.image, self.draw_buffer]): self._paint(painter, x, y) if self.x_sym: self._paint(painter, abs(max_x - x - self.cursor_size), y) if self.y_sym: self._paint(painter, x, abs(max_y - y - self.cursor_size)) if (self.x_sym and self.y_sym) or self.opp_sym: self._paint(painter, abs(max_x - x - self.cursor_size), abs(max_y - y - self.cursor_size)) del painter self.update() def sizeHint(self): if not self.image: return super().minimumSizeHint() return self.image.size() def on_y_toggle(self, y_state): self.y_sym = y_state def on_x_toggle(self, x_state): self.x_sym = x_state def on_opp_toggle(self, opp_state): self.opp_sym = opp_state
def update(self): self.menu.clear() try: version = self.app.provider.get_api_version() except ( # dbus raise some magic dbus.exceptions.UnknownMethodException, dbus.exceptions.DBusException, ): version = -1 if version != API_VERSION: action = self.menu.addAction( self.tr('API version missmatch, please restart'), ) action.setEnabled(False) if version < API_VERSION: handler = self.app.provider.kill else: handler = partial(os.execlp, 'everpad', '--replace') self.menu.addAction( self.tr('Restart everpad'), handler, ) return if self.app.provider.is_authenticated(): pin_notes = self.app.provider.find_notes( '', dbus.Array([], signature='i'), dbus.Array([], signature='i'), 0, 20, Note.ORDER_UPDATED_DESC, 1, ) notes = self.app.provider.find_notes( '', dbus.Array([], signature='i'), dbus.Array([], signature='i'), 0, 20 - len(pin_notes), Note.ORDER_UPDATED_DESC, 0, ) first_sync = not (len(notes) + len(pin_notes) or self.app.provider.is_first_synced()) status_syncing = self.app.provider.get_status() == STATUS_SYNC if status_syncing and first_sync: sync_label = self.tr('Wait, first sync in progress') elif status_syncing and not first_sync: sync_label = self.tr('Sync in progress') elif not status_syncing and first_sync: sync_label = self.tr('Please perform first sync') else: last_sync = self.app.provider.get_last_sync() delta_sync = (datetime.now() - datetime.strptime( last_sync, '%H:%M')).seconds // 60 if delta_sync == 0: sync_label = self.tr('Last Sync: Just now') elif delta_sync == 1: sync_label = self.tr('Last Sync: %s min ago') % delta_sync else: sync_label = self.tr('Last Sync: %s mins ago') % delta_sync menu_items = { 'create_note': [self.tr('Create Note'), self.create], 'all_notes': [self.tr('All Notes'), self.show_all_notes], 'sync': [sync_label, Slot()(self.app.provider.sync)], 'pin_notes': pin_notes, 'notes': notes, } for item in self.app.settings.value('menu-order', DEFAULT_INDICATOR_LAYOUT): if item == 'pin_notes' or item == 'notes': if not first_sync: if len(menu_items[item]): self.menu.addSeparator() for struct in menu_items[item]: self._add_note(struct) self.menu.addSeparator() else: action = self.menu.addAction(menu_items[item][0], menu_items[item][1]) if status_syncing and item == 'sync': action.setEnabled(False) self.menu.addSeparator() self.menu.addAction(self.tr('Settings and Management'), self.show_management) self.menu.addAction(self.tr('Exit'), self.exit)
def update(self): self.menu.clear() try: version = self.app.provider.get_api_version() except ( # dbus raise some magic dbus.exceptions.UnknownMethodException, dbus.exceptions.DBusException, ): version = -1 if version != API_VERSION: action = self.menu.addAction( self.tr('API version missmatch, please restart'), ) action.setEnabled(False) self.menu.addAction( self.tr('Restart everpad'), self.kill_all, ) return if get_auth_token(): pin_notes = self.app.provider.find_notes( '', dbus.Array([], signature='i'), dbus.Array([], signature='i'), 0, 20, Note.ORDER_UPDATED_DESC, 1, ) notes = self.app.provider.find_notes( '', dbus.Array([], signature='i'), dbus.Array([], signature='i'), 0, 20 - len(pin_notes), Note.ORDER_UPDATED_DESC, 0, ) if len(notes) + len( pin_notes) or self.app.provider.is_first_synced(): self.menu.addAction(self.tr('All Notes'), self.show_all_notes) self.menu.addSeparator() if len(pin_notes): for struct in pin_notes: self._add_note(struct) self.menu.addSeparator() for struct in notes: self._add_note(struct) self.menu.addSeparator() self.menu.addAction(self.tr('Create Note'), self.create) first_sync = False else: first_sync = True if self.app.provider.get_status() == STATUS_SYNC: action = self.menu.addAction( self.tr('Wait, first sync in progress' ) if first_sync else self.tr('Sync in progress')) action.setEnabled(False) else: if first_sync: label = self.tr('Please perform first sync') else: label = self.tr( 'Last sync: %s') % self.app.provider.get_last_sync() self.menu.addAction(label, Slot()(self.app.provider.sync)) self.menu.addAction(self.tr('Settings and Management'), self.show_management) self.menu.addSeparator() self.menu.addAction(self.tr('Exit'), self.exit)
def update(self): self.menu.clear() try: version = self.app.provider.get_api_version() except ( # dbus raise some magic dbus.exceptions.UnknownMethodException, dbus.exceptions.DBusException, ): version = -1 if version != API_VERSION: action = self.menu.addAction( self.tr('API version missmatch, please restart'), ) action.setEnabled(False) if version < API_VERSION: handler = self.app.provider.kill else: handler = partial(os.execlp, 'everpad', '--replace') self.menu.addAction( self.tr('Restart everpad'), handler, ) return if self.app.provider.is_authenticated(): pin_notes = self.app.provider.find_notes( '', dbus.Array([], signature='i'), dbus.Array([], signature='i'), 0, 20, Note.ORDER_UPDATED_DESC, 1, ) notes = self.app.provider.find_notes( '', dbus.Array([], signature='i'), dbus.Array([], signature='i'), 0, 20 - len(pin_notes), Note.ORDER_UPDATED_DESC, 0, ) if len(notes) + len( pin_notes) or self.app.provider.is_first_synced(): self.menu.addAction(self.tr('All Notes'), self.show_all_notes) self.menu.addSeparator() if len(pin_notes): for struct in pin_notes: self._add_note(struct) self.menu.addSeparator() for struct in notes: self._add_note(struct) self.menu.addSeparator() self.menu.addAction(self.tr('Create Note'), self.create) first_sync = False else: first_sync = True if self.app.provider.get_status() == STATUS_SYNC: action = self.menu.addAction( self.tr('Wait, first sync in progress' ) if first_sync else self.tr('Sync in progress')) action.setEnabled(False) else: if first_sync: label = self.tr('Please perform first sync') else: last_sync = self.app.provider.get_last_sync() delta_sync = (datetime.now() - datetime.strptime( last_sync, '%H:%M')).seconds // 60 if delta_sync == 0: label = self.tr('Last Sync: Just now') elif delta_sync == 1: label = self.tr('Last Sync: %s min ago') % delta_sync else: label = self.tr('Last Sync: %s mins ago') % delta_sync self.menu.addAction(label, Slot()(self.app.provider.sync)) self.menu.addAction(self.tr('Settings and Management'), self.show_management) self.menu.addSeparator() self.menu.addAction(self.tr('Exit'), self.exit)