示例#1
0
    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)
示例#2
0
	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)
示例#3
0
    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()
示例#4
0
    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()
示例#5
0
	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'])
示例#6
0
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_())
示例#7
0
    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)
示例#8
0
	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
		}
示例#9
0
	def __init__(self, parent, calendar):
		super().__init__(parent=parent)
		self.__calendar = calendar
		self.__settings = Settings()
		self.__storage = Storage()
示例#10
0
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()
示例#11
0
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')
		))
示例#12
0
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)
示例#13
0
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)