def __init__(self, flags, *args, **kwargs): super(BackupDialog, self).__init__(flags=flags, *args) if 'palette' in kwargs: self.setPalette(kwargs.get('palette')) if 'font' in kwargs: self.setFont(kwargs.get('font')) self.setWindowFlags(Qt.Dialog) self.spinner = WaitingSpinner() self.thread_pool = QThreadPool() self.calendar = kwargs.get('calendar', None) if self.calendar is None: raise RuntimeError('BackupDialog: calendar is not set') self.storage = kwargs.get('storage', Storage()) self.cloud = kwargs.get('cloud_storage', CloudStorage()) self.setFixedSize(500, 320) self.setWindowTitle(self.tr('Backup and Restore')) self.backups_pool = [] self.settings = Settings() self.backup_file_input = QLineEdit() self.restore_file_input = QLineEdit() self.backup_file_button = PushButton('', 40, 30, self.get_folder_path) self.restore_file_button = PushButton('', 40, 30, self.get_file_path) self.launch_restore_button = PushButton(self.tr('Start'), 120, 35, self.launch_restore_local) self.launch_backup_button = PushButton(self.tr('Start'), 120, 35, self.launch_backup_local) self.backups_cloud_list_widget = QListWidget() self.upload_backup_button = PushButton(' {}'.format(self.tr('Upload')), 120, 35, self.upload_backup_cloud) self.download_backup_button = PushButton( ' {}'.format(self.tr('Download')), 150 if self.settings.app_lang == 'uk_UA' else 120, 35, self.download_backup_cloud) self.delete_backup_button = PushButton(' {}'.format(self.tr('Delete')), 120, 35, self.delete_backup_cloud) self.setup_ui() self.layout().addWidget(self.spinner)
def __init__(self, **kwargs): self.settings = Settings() super().__init__(None, Qt.WindowFlags()) self.window().setWindowTitle(APP_NAME) self.resize(self.settings.app_size) self.move(self.settings.app_pos) self.setWindowIcon(self.settings.app_icon()) self.setMinimumWidth(APP_MIN_WIDTH) self.setMinimumHeight(APP_MIN_HEIGHT) self.events_list = EventListWidget(**{ 'parent': self, }) # noinspection PyUnresolvedReferences self.events_list.itemSelectionChanged.connect(self.events_list_selection_changed) self.calendar = self.init_calendar() self.btn_new_event = PushButton(self.tr('New'), 90, 30, self.calendar.open_details_event) self.btn_edit = PushButton(self.tr('Details'), 90, 30, self.calendar.edit_event_click) self.btn_delete = PushButton(self.tr('Delete'), 90, 30, self.calendar.delete_event_click) events_widget = self.init_events_widget() main_layout = QHBoxLayout() # noinspection PyArgumentList main_layout.addWidget(self.calendar) main_layout.addWidget(events_widget, alignment=Qt.AlignRight) central_widget = QWidget(flags=self.windowFlags()) central_widget.setLayout(main_layout) self.setCentralWidget(central_widget) # noinspection PyUnresolvedReferences self.calendar.selectionChanged.connect(self.date_selection_changed) self.setup_navigation_menu() self.setFont(QFont('SansSerif', self.settings.app_font)) self.open_action = QAction('{} {}'.format(self.tr('Open'), APP_NAME), self) self.hide_action = QAction(self.tr('Minimize To Tray'), self) if self.settings.start_in_tray: self.hide_action.setEnabled(False) self.close_action = QAction('{} {}'.format(self.tr('Quit'), APP_NAME), self) self.tray_icon = self.init_tray(kwargs.get('app')) self.tray_icon.show() self.setPalette(self.settings.app_theme)
def __init__(self, flags, *args, **kwargs): super(AboutDialog, self).__init__(flags=flags, *args) if 'palette' in kwargs: self.setPalette(kwargs.get('palette')) self.setFixedSize(500, 250) self.setWindowTitle('Legal Information') self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint) self.calendar = kwargs.get('calendar', None) if self.calendar is None: raise RuntimeError('AboutDialog: calendar is not set') self.settings = Settings() self.user = None self.thread_pool = QThreadPool() self.data_section = QVBoxLayout() self.setup_ui()
def __init__(self, parent, **kwargs): super().__init__(parent=parent) self.parent = parent self.setGeometry(0, 0, kwargs['width'], kwargs['height']) self.setGridVisible(True) # noinspection PyUnresolvedReferences self.clicked[QDate].connect(self.load_events) self.setContentsMargins(0, 0, 0, 0) self.events_list = kwargs.get('events_list', None) if self.events_list is None: raise RuntimeError('CalendarWidget: events list is not set') self.storage = Storage() self.thread_pool = QThreadPool() self.settings = Settings() font = QFont('SansSerif', self.settings.app_font) self.setFont(font) self.setPalette(self.settings.app_theme) self.cloud_storage = CloudStorage() params = { 'font': font, 'calendar': self, 'flags': self.parent.windowFlags(), 'palette': self.settings.app_theme, 'parent': self } self.event_details_dialog = EventDetailsDialog(storage=self.storage, **params) self.settings_dialog = SettingsDialog(cloud_storage=self.cloud_storage, **params) self.backup_dialog = BackupDialog(storage=self.storage, cloud_storage=self.cloud_storage, **params) self.dialogs = [ self.event_details_dialog, self.settings_dialog, self.backup_dialog ] self.marked_dates = [] self.past_events = [] self.update()
def restore_from_dict(self, data): err_template = 'Restore failure: {}.' for key in ['digest', 'timestamp', 'backup']: if key not in data: raise DatabaseException(err_template.format('invalid backup file')) if datetime.now() < datetime.strptime(data['timestamp'], EventModel.TIMESTAMP_FORMAT): raise DatabaseException(err_template.format('incorrect timestamp')) backup_decoded = base64.b64decode(data['backup']) if sha512(backup_decoded).hexdigest() != data['digest']: raise DatabaseException(err_template.format('backup is broken')) backup = json.loads(backup_decoded.decode('utf8')) if 'db' not in backup: raise DatabaseException(err_template.format('invalid backup data')) self.__cursor.execute(QUERY_DELETE_ALL_EVENTS) self.from_array(backup['db']) if 'settings' in backup: Settings().from_dict(backup['settings'])
def main(): app = QApplication(sys.argv) app.setStyle('Fusion') settings = Settings() translator = QTranslator() translator.load(':/lang/{}.qm'.format(settings.app_lang)) app.installTranslator(translator) window = MainWindow(app=app) ReminderService(window, window.calendar).start() if not settings.start_in_tray: window.show() sys.exit(app.exec_())
def __init__(self, flags, *args, **kwargs): super(AccountDialog, self).__init__(flags=flags, *args) if 'palette' in kwargs: self.setPalette(kwargs.get('palette')) if 'font' in kwargs: self.setFont(kwargs.get('font')) self.calendar = kwargs.get('calendar', None) if self.calendar is None: raise RuntimeError('AccountDialog: calendar is not set') self.setFixedSize(550, 280) self.setWindowTitle(self.tr('Account')) self.setWindowFlags(Qt.Dialog) self.settings = Settings() self.spinner = WaitingSpinner() self.thread_pool = QThreadPool() self.cloud = kwargs.get('cloud_storage', CloudStorage()) self.username_signup_input = QLineEdit() self.email_signup_input = QLineEdit() self.username_login_input = QLineEdit() self.password_login_input = QLineEdit() self.remember_login_check_box = QCheckBox(self.tr('Remember me')) self.login_menu = None self.account_info_menu = None self.v_layout = QVBoxLayout() self.tabs = QTabWidget(self) self.setup_ui() self.layout().addWidget(self.spinner)
def prepare_backup_data(events_array, timestamp, include_settings, username=None, settings=Settings().to_dict()): data = { 'db': events_array } if include_settings: data['settings'] = settings if username is not None: data['username'] = username data = json.dumps(data).encode('utf8') encoded_data = base64.b64encode(data) return { 'digest': sha512(data).hexdigest(), 'timestamp': timestamp, 'backup': encoded_data, 'backup_size': Storage.count_str_size(encoded_data), 'events_count': len(events_array), 'contains_settings': include_settings }
def __init__(self, parent, calendar): super().__init__(parent=parent) self.__calendar = calendar self.__settings = Settings() self.__storage = Storage()
class ReminderService(QThread): def __init__(self, parent, calendar): super().__init__(parent=parent) self.__calendar = calendar self.__settings = Settings() self.__storage = Storage() def run(self): try: self.__storage.connect() try: self.__process_events(today=date.today()) except Exception as exc: logger.error(log_msg('Service error, can not process non-today events: {}'.format(exc))) while True: try: time.sleep(1) self.__process_events(today=date.today(), date_filter=date.today()) except Exception as exc: logger.error(log_msg('Processing event error: {}'.format(exc))) except Exception as exc: logger.error(log_msg('Service error: {}'.format(exc), 8)) finally: self.storage.disconnect() def __process_events(self, today, date_filter=None): remind_time = self.__settings.remind_time_before_event(True) events = self.__storage.get_events(date_filter, delta=remind_time) need_to_update = False for event in events: now = datetime.now() now_plus_delta = ( now + timedelta(minutes=remind_time if remind_time >= 1 else 0) ).replace(microsecond=0) if event.is_past is False and ((event.date == now.date() and event.time <= now_plus_delta.time()) or event.date < now_plus_delta.date()): if event.expired(now): self.__send_notification(event) if event.repeat_weekly is True: new_date = event.date while new_date <= today: new_date += timedelta(days=7) self.__storage.update_event(pk=event.id, e_date=new_date) else: if self.__settings.remove_event_after_time_up is True: self.__storage.delete_event(event.id) else: self.__storage.update_event(pk=event.id, is_past=True, is_notified=1) need_to_update = True else: if event.is_notified == 0: self.__send_notification(event) self.__storage.update_event(pk=event.id, is_notified=1) if need_to_update: self.__calendar.update() def __send_notification(self, event): Notification( title=APP_NAME, icon_path=self.__settings.app_icon(not system.is_linux(), q_icon=False, small=True), description='{}\n\n{}'.format(event.title, event.description), duration=self.__settings.notification_duration, urgency=Notification.URGENCY_CRITICAL ).send()
class MainWindow(QMainWindow): def __init__(self, **kwargs): self.settings = Settings() super().__init__(None, Qt.WindowFlags()) self.window().setWindowTitle(APP_NAME) self.resize(self.settings.app_size) self.move(self.settings.app_pos) self.setWindowIcon(self.settings.app_icon()) self.setMinimumWidth(APP_MIN_WIDTH) self.setMinimumHeight(APP_MIN_HEIGHT) self.events_list = EventListWidget(**{ 'parent': self, }) # noinspection PyUnresolvedReferences self.events_list.itemSelectionChanged.connect(self.events_list_selection_changed) self.calendar = self.init_calendar() self.btn_new_event = PushButton(self.tr('New'), 90, 30, self.calendar.open_details_event) self.btn_edit = PushButton(self.tr('Details'), 90, 30, self.calendar.edit_event_click) self.btn_delete = PushButton(self.tr('Delete'), 90, 30, self.calendar.delete_event_click) events_widget = self.init_events_widget() main_layout = QHBoxLayout() # noinspection PyArgumentList main_layout.addWidget(self.calendar) main_layout.addWidget(events_widget, alignment=Qt.AlignRight) central_widget = QWidget(flags=self.windowFlags()) central_widget.setLayout(main_layout) self.setCentralWidget(central_widget) # noinspection PyUnresolvedReferences self.calendar.selectionChanged.connect(self.date_selection_changed) self.setup_navigation_menu() self.setFont(QFont('SansSerif', self.settings.app_font)) self.open_action = QAction('{} {}'.format(self.tr('Open'), APP_NAME), self) self.hide_action = QAction(self.tr('Minimize To Tray'), self) if self.settings.start_in_tray: self.hide_action.setEnabled(False) self.close_action = QAction('{} {}'.format(self.tr('Quit'), APP_NAME), self) self.tray_icon = self.init_tray(kwargs.get('app')) self.tray_icon.show() self.setPalette(self.settings.app_theme) def closeEvent(self, event): event.ignore() self.hide() def date_selection_changed(self): if self.calendar.selectedDate().toPyDate() < datetime.today().date(): self.btn_new_event.setEnabled(False) else: self.btn_new_event.setEnabled(True) def save_state(self): self.settings.autocommit(False) self.settings.set_pos(self.pos()) self.settings.set_size(self.size()) self.settings.commit() def quit_app(self): self.save_state() qApp.quit() def init_tray(self, app): actions = { self.open_action: self.show, self.hide_action: self.hide, self.close_action: self.quit_app } tray_menu = QMenu() for key, value in actions.items(): # noinspection PyUnresolvedReferences key.triggered.connect(value) tray_menu.addAction(key) tray_icon = QSystemTrayIcon(self.settings.app_icon(), app) tray_icon.setContextMenu(tray_menu) return tray_icon def init_calendar(self): calendar = CalendarWidget( self, width=self.width() - 400, height=self.height(), events_list=self.events_list ) calendar.setLocale(QLocale(AVAILABLE_LOCALES[self.settings.app_lang])) calendar.setFirstDayOfWeek(Qt.Monday) calendar.setSelectedDate(datetime.now()) return calendar def init_events_widget(self): scroll_view = QScrollArea() scroll_view.setWidget(self.events_list) scroll_view.setWidgetResizable(True) scroll_view.setFixedWidth(400) layout = QVBoxLayout() # noinspection PyArgumentList layout.addWidget(scroll_view) buttons = QHBoxLayout() buttons.setAlignment(Qt.AlignRight | Qt.AlignBottom) buttons.addWidget(self.btn_new_event, 0, Qt.AlignLeft) self.btn_edit.setEnabled(False) buttons.addWidget(self.btn_edit, 0, Qt.AlignCenter) self.btn_delete.setEnabled(False) buttons.addWidget(self.btn_delete, 0, Qt.AlignRight) layout.addLayout(buttons) widget = QWidget(flags=self.windowFlags()) widget.setLayout(layout) return widget def events_list_selection_changed(self): if self.events_list.selected_item is not None: if len(self.events_list.selected_ids()) == 1: self.btn_edit.setEnabled(True) else: self.btn_edit.setEnabled(False) self.btn_delete.setEnabled(True) else: self.btn_edit.setEnabled(False) self.btn_delete.setEnabled(False) def hide(self): self.hide_action.setEnabled(False) self.open_action.setEnabled(True) super(MainWindow, self).hide() def show(self): self.hide_action.setEnabled(True) self.open_action.setEnabled(False) super(MainWindow, self).show() def setup_navigation_menu(self): main_menu = self.menuBar() self.setup_file_menu(main_menu) self.setup_help_menu(main_menu) @staticmethod def create_action(target, title, fn, shortcut=None, tip=None, icon=None): action = QAction(title, target) if shortcut: action.setShortcut(shortcut) if tip: action.setStatusTip(tip) if icon: action.setIcon(icon) # noinspection PyUnresolvedReferences action.triggered.connect(fn) return action def create_shortcut(self): try: shortcut_icon.create() info(self, self.tr('Shortcut icon has been created')) except ShortcutIconIsNotSupportedError: error(self, self.tr('Shortcut icon is not supported on {} by application').format(system.name())) except Exception as exc: logger.error(log_msg('Unable to create shortcut icon: {}'.format(exc))) error(self, self.tr('Unable to create shortcut icon')) def setup_file_menu(self, main_menu): file_menu = main_menu.addMenu('&{}'.format(self.tr('File'))) file_menu.addAction( self.create_action( self, '{}...'.format(self.tr('New Event')), self.calendar.open_details_event, 'Ctrl+N' ) ) file_menu.addAction( self.create_action( self, '{}...'.format(self.tr('Se{}ttings').format('&')), self.calendar.open_settings, 'Ctrl+Alt+S', icon=qta.icon('mdi.settings') ) ) file_menu.addAction(self.create_action( self, '&{}'.format(self.tr('Create shortcut icon...')), self.create_shortcut, icon=qta.icon('mdi.desktop-mac') )) file_menu.addAction( self.create_action( self, '{}...'.format(self.tr('Backup and Restore')), self.calendar.open_backup_and_restore, 'Ctrl+Alt+B', icon=qta.icon('mdi.backup-restore') ) ) def setup_help_menu(self, main_menu): help_menu = main_menu.addMenu('&{}'.format(self.tr('Help'))) help_menu.addAction(self.create_action( self, '&{}...'.format(self.tr('Account')), self.calendar.open_account_info, icon=qta.icon('mdi.account-circle') )) help_menu.addAction(self.create_action( self, '&{}'.format(self.tr('About')), self.calendar.open_about, icon=qta.icon('mdi.information-outline') ))
class BackupDialog(QDialog): def __init__(self, flags, *args, **kwargs): super(BackupDialog, self).__init__(flags=flags, *args) if 'palette' in kwargs: self.setPalette(kwargs.get('palette')) if 'font' in kwargs: self.setFont(kwargs.get('font')) self.setWindowFlags(Qt.Dialog) self.spinner = WaitingSpinner() self.thread_pool = QThreadPool() self.calendar = kwargs.get('calendar', None) if self.calendar is None: raise RuntimeError('BackupDialog: calendar is not set') self.storage = kwargs.get('storage', Storage()) self.cloud = kwargs.get('cloud_storage', CloudStorage()) self.setFixedSize(500, 320) self.setWindowTitle(self.tr('Backup and Restore')) self.backups_pool = [] self.settings = Settings() self.backup_file_input = QLineEdit() self.restore_file_input = QLineEdit() self.backup_file_button = PushButton('', 40, 30, self.get_folder_path) self.restore_file_button = PushButton('', 40, 30, self.get_file_path) self.launch_restore_button = PushButton(self.tr('Start'), 120, 35, self.launch_restore_local) self.launch_backup_button = PushButton(self.tr('Start'), 120, 35, self.launch_backup_local) self.backups_cloud_list_widget = QListWidget() self.upload_backup_button = PushButton(' {}'.format(self.tr('Upload')), 120, 35, self.upload_backup_cloud) self.download_backup_button = PushButton( ' {}'.format(self.tr('Download')), 150 if self.settings.app_lang == 'uk_UA' else 120, 35, self.download_backup_cloud) self.delete_backup_button = PushButton(' {}'.format(self.tr('Delete')), 120, 35, self.delete_backup_cloud) self.setup_ui() self.layout().addWidget(self.spinner) def showEvent(self, event): self.move(self.calendar.window().frameGeometry().topLeft() + self.calendar.window().rect().center() - self.rect().center()) self.refresh_backups_cloud() btn_color = 'white' if self.settings.is_dark_theme else 'black' self.backup_file_button.setIcon( qta.icon('mdi.folder-open', color=btn_color, scale_factor=1.5)) self.restore_file_button.setIcon( qta.icon('mdi.file-plus', color=btn_color, scale_factor=1.5)) self.upload_backup_button.setIcon( qta.icon('mdi.cloud-upload', color=btn_color, scale_factor=1.2)) self.download_backup_button.setIcon( qta.icon('mdi.cloud-download', color=btn_color, scale_factor=1.2)) self.delete_backup_button.setIcon( qta.icon('mdi.delete', color=btn_color, scale_factor=1.2)) super(BackupDialog, self).showEvent(event) def setup_ui(self): tabs_widget = QTabWidget(self) tabs_widget.setMinimumWidth(self.width() - 22) self.setup_cloud_ui(tabs_widget) self.setup_local_ui(tabs_widget) content = QVBoxLayout() content.addWidget(tabs_widget, alignment=Qt.AlignLeft) self.setLayout(content) def setup_local_ui(self, tabs): local_backup_tab = QTabWidget(self) self.setup_local_backup_ui(local_backup_tab) self.setup_local_restore_ui(local_backup_tab) tabs.addTab(local_backup_tab, self.tr('Local')) def setup_local_backup_ui(self, tabs): tab = QWidget(flags=tabs.windowFlags()) h2_layout = QHBoxLayout() # noinspection PyArgumentList h2_layout.addWidget(QLabel('{}:'.format(self.tr('Location')))) h2_layout.setContentsMargins(10, 0, 10, 20) if os.path.exists(self.settings.app_last_backup_path): self.backup_file_input.setText(self.settings.app_last_backup_path) # noinspection PyArgumentList h2_layout.addWidget(self.backup_file_input) # noinspection PyArgumentList h2_layout.addWidget(self.backup_file_button) layout = QVBoxLayout() layout.setAlignment(Qt.AlignCenter) label = QLabel(self.tr('Create full backup of your calendar notes')) label.setContentsMargins(0, 10, 0, 30) layout.addWidget(label, alignment=Qt.AlignCenter) layout.addLayout(h2_layout) layout.addWidget(self.launch_backup_button, alignment=Qt.AlignCenter) tab.setLayout(layout) tabs.addTab(tab, self.tr('Backup')) def setup_local_restore_ui(self, tabs): tab = QWidget(flags=tabs.windowFlags()) h2_layout = QHBoxLayout() # noinspection PyArgumentList h2_layout.addWidget(QLabel('{}:'.format(self.tr('Location')))) h2_layout.setContentsMargins(10, 0, 10, 20) if os.path.isfile(self.settings.app_last_restore_path): self.restore_file_input.setText( self.settings.app_last_restore_path) # noinspection PyArgumentList h2_layout.addWidget(self.restore_file_input) # noinspection PyArgumentList h2_layout.addWidget(self.restore_file_button) layout = QVBoxLayout() layout.setAlignment(Qt.AlignCenter) label = QLabel( self.tr('Restore all your calendar notes with backup file')) label.setWordWrap(True) label.setContentsMargins(0, 10, 0, 30) layout.addWidget(label, alignment=Qt.AlignCenter) layout.addLayout(h2_layout) layout.addWidget(self.launch_restore_button, alignment=Qt.AlignCenter) tab.setLayout(layout) tabs.addTab(tab, self.tr('Restore')) def get_folder_path(self): # noinspection PyArgumentList file_name = QFileDialog().getExistingDirectory( caption=self.tr('Select Directory'), directory=self.backup_file_input.text()) if len(file_name) > 0: self.backup_file_input.setText(str(file_name)) def get_file_path(self): path = self.restore_file_input.text() # noinspection PyArgumentList file_name = QFileDialog().getOpenFileName( caption=self.tr('Open file'), directory=path if os.path.isfile(path) else './', filter='(*.bak)') if len(file_name) > 0: self.restore_file_input.setText(file_name[0]) def launch_restore_local(self): path = self.restore_file_input.text() self.settings.set_last_restore_path(path) self.exec_worker(self.storage.restore, self.launch_restore_local_success, None, *(path, )) def launch_restore_local_success(self): self.calendar.update() self.calendar.settings_dialog.refresh_settings_values() popup.info(self, self.tr('Data has been restored')) def launch_backup_local(self): path = self.backup_file_input.text() self.settings.set_last_backup_path(path) self.exec_worker(self.storage.backup, self.launch_backup_local_success, None, *(path, self.settings.include_settings_backup)) def launch_backup_local_success(self): popup.info(self, self.tr('Backup has been created')) def setup_cloud_ui(self, tabs): tab = QWidget(flags=tabs.windowFlags()) layout = QVBoxLayout() layout.setAlignment(Qt.AlignBottom) buttons_layout = QHBoxLayout() center_buttons_layout = QHBoxLayout() center_buttons_layout.setAlignment(Qt.AlignLeft) self.upload_backup_button.setToolTip(self.tr('Upload')) self.upload_backup_button.setEnabled(False) center_buttons_layout.addWidget(self.upload_backup_button, alignment=Qt.AlignCenter) self.download_backup_button.setToolTip(self.tr('Download')) self.download_backup_button.setEnabled(False) center_buttons_layout.addWidget(self.download_backup_button, alignment=Qt.AlignCenter) buttons_layout.addLayout(center_buttons_layout) self.delete_backup_button.setEnabled(False) self.delete_backup_button.setToolTip(self.tr('Delete')) buttons_layout.addWidget(self.delete_backup_button, alignment=Qt.AlignRight) layout.addLayout(buttons_layout) scroll_view = QScrollArea() # noinspection PyUnresolvedReferences self.backups_cloud_list_widget.itemSelectionChanged.connect( self.selection_changed) scroll_view.setWidget(self.backups_cloud_list_widget) scroll_view.setWidgetResizable(True) scroll_view.setFixedHeight(200) scroll_view.setFixedWidth(455) layout.addWidget(scroll_view, alignment=Qt.AlignLeft) tab.setLayout(layout) tabs.addTab(tab, self.tr('Cloud')) def selection_changed(self): if len(self.backups_cloud_list_widget.selectedItems()) > 0: self.delete_backup_button.setEnabled(True) self.download_backup_button.setEnabled(True) else: self.delete_backup_button.setEnabled(False) self.download_backup_button.setEnabled(False) def refresh_backups_cloud(self): self.backups_cloud_list_widget.clear() self.exec_worker(self.cloud.backups, None, self.refresh_backups_cloud_success) def refresh_backups_cloud_success(self, backups): self.upload_backup_button.setEnabled(True) for backup in backups: self.add_backup_widget(backup) def add_backup_widget(self, backup_data): num_repr = repr(backup_data['events_count']) if len(num_repr) > 1 and int(num_repr[-2]) == 1: text_label = self.tr('events') elif 1 < int(num_repr[-1]) < 5: text_label = self.tr('events*') else: text_label = self.tr('event{}'.format( 's' if int(num_repr[-1]) > 1 or backup_data['events_count'] % 2 == 0 else '')) backup_widget = BackupWidget( flags=self.backups_cloud_list_widget.windowFlags(), parent=self.backups_cloud_list_widget, palette=self.palette(), font=self.font(), hash_sum=backup_data['digest'], title=datetime.strptime(backup_data['timestamp'], EventModel.TIMESTAMP_FORMAT).strftime( EventModel.DATE_TIME_FORMAT), description='{} {} {}, {}'.format( backup_data['backup_size'], backup_data['events_count'], text_label, self.tr('full backup') if backup_data['contains_settings'] is True else self.tr('excluded settings'))) list_widget_item = QListWidgetItem(self.backups_cloud_list_widget) list_widget_item.setSizeHint(backup_widget.sizeHint()) self.backups_cloud_list_widget.addItem(list_widget_item) self.backups_cloud_list_widget.setItemWidget(list_widget_item, backup_widget) def upload_backup_cloud(self): self.exec_worker(self.upload_backup_cloud_run, self.upload_backup_cloud_success, None) def upload_backup_cloud_run(self): user = self.cloud.user() timestamp = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S') backup_data = self.storage.prepare_backup_data( self.storage.to_array(), timestamp, self.settings.include_settings_backup, user['username']) self.cloud.upload_backup(backup_data) def upload_backup_cloud_success(self): self.refresh_backups_cloud() popup.info(self, self.tr('Backup was successfully uploaded to the cloud')) def download_backup_cloud(self): current = self.get_current_selected() if current is not None: self.exec_worker(self.download_backup_cloud_run, self.download_backup_cloud_success, None, *(current, )) def download_backup_cloud_run(self, current): self.storage.restore_from_dict( self.cloud.download_backup(current.hash_sum)) def download_backup_cloud_success(self): self.calendar.reset_palette(self.settings.app_theme) self.calendar.reset_font(QFont('SansSerif', self.settings.app_font)) self.calendar.update() self.calendar.settings_dialog.refresh_settings_values() popup.info( self, self. tr('Backup was successfully downloaded. Restart application to enable all restored settings.' )) def delete_backup_cloud(self): current = self.get_current_selected() if current is not None: self.exec_worker(self.cloud.delete_backup, self.delete_backup_cloud_success, None, *(current.hash_sum, )) def delete_backup_cloud_success(self): self.refresh_backups_cloud() popup.info(self, self.tr('Backup was deleted successfully')) def get_current_selected(self): selected_items = self.backups_cloud_list_widget.selectedItems() if len(selected_items) > 0: return self.backups_cloud_list_widget.itemWidget(selected_items[0]) return None def exec_worker(self, fn, fn_success, fn_param_success, *args, **kwargs): self.spinner.start() worker = Worker(fn, *args, **kwargs) if fn_success is not None: worker.signals.success.connect(fn_success) if fn_param_success is not None: worker.signals.param_success.connect(fn_param_success) worker.signals.error.connect(self.popup_error) worker.signals.finished.connect(self.spinner.stop) self.thread_pool.start(worker) def popup_error(self, err): try: raise err[0](err[1]) except AuthRequiredError: err_msg = self.tr( 'Account access failure: authentication is required') except UserRetrievingError: err_msg = '{} {}'.format( self. tr('Reading account failure: unable to retrieve account information, status' ), err[1]) except ReadingBackupsError: err_msg = '{} {}'.format( self. tr('Reading backups failure: unable to retrieve backups data from the server, status' ), err[1]) except BackupAlreadyExistsError: err_msg = self.tr('Upload failure: backup already exists') except BackupDownloadingError: err_msg = self.tr('Download failure: unable to download backup') except BackupDeletingError: err_msg = self.tr('Deleting failure: unable to delete backup') except (CloudStorageException, RequestException, Exception): err_msg = str(err[1]) popup.error(self, err_msg)
class AboutDialog(QDialog): def __init__(self, flags, *args, **kwargs): super(AboutDialog, self).__init__(flags=flags, *args) if 'palette' in kwargs: self.setPalette(kwargs.get('palette')) self.setFixedSize(500, 250) self.setWindowTitle('Legal Information') self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint) self.calendar = kwargs.get('calendar', None) if self.calendar is None: raise RuntimeError('AboutDialog: calendar is not set') self.settings = Settings() self.user = None self.thread_pool = QThreadPool() self.data_section = QVBoxLayout() self.setup_ui() def showEvent(self, event): self.move(self.calendar.window().frameGeometry().topLeft() + self.calendar.window().rect().center() - self.rect().center()) super(AboutDialog, self).showEvent(event) def setup_ui(self): content = QVBoxLayout() title_section = QHBoxLayout() title_section.setContentsMargins(0, 0, 0, 30) title_section.setSpacing(20) logo_image = QLabel() logo_image.setPixmap( QPixmap(self.settings.app_icon(q_icon=False, small=True))) logo_image.setFixedSize(QSize(70, 70)) # noinspection PyArgumentList title_section.addWidget(logo_image) name_label = QLabel(APP_NAME) name_label.setFont(QFont('SansSerif', 26)) title_section.addWidget(name_label, alignment=Qt.AlignVCenter) bold_font = QFont('SansSerif', 11) bold_font.setBold(True) self.data_section.setSpacing(0) version_label = QLabel('{} {}'.format(APP_NAME, APP_VERSION)) version_label.setFont(bold_font) self.data_section.addWidget(version_label, alignment=Qt.AlignTop) self.data_section.addWidget(QLabel( 'Released on {}'.format(APP_RELEASE_DATE)), alignment=Qt.AlignTop) worker = Worker(self.get_user) worker.signals.success.connect(self.get_user_success) self.thread_pool.start(worker) content.addLayout(title_section) content.addLayout(self.data_section) content.addWidget(QLabel( 'Copyright (c) 2019 {}'.format(APP_ORGANIZATION)), alignment=Qt.AlignBottom) self.setLayout(content) def get_user(self): self.user = CloudStorage().user() def get_user_success(self): self.data_section.addWidget(QLabel( 'Copy of this software is distributed to {}.'.format( self.user.get('username'))), alignment=Qt.AlignTop)