Files
Lector/lector/settingsdialog.py
BasioMeusPuga 5081a31f1a Fine tune progress display
Option: Set consider read at percentage
Small fixes
2018-03-31 10:31:57 +05:30

463 lines
17 KiB
Python

#!/usr/bin/env python3
# This file is a part of Lector, a Qt based ebook reader
# Copyright (C) 2017 BasioMeusPuga
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# TODO
# Get Cancel working with the file system model
import os
import copy
import pathlib
from PyQt5 import QtWidgets, QtCore, QtGui
from lector import database
from lector.models import MostExcellentFileSystemModel
from lector.threaded import BackGroundBookSearch, BackGroundBookAddition
from lector.resources import settingswindow
class SettingsUI(QtWidgets.QDialog, settingswindow.Ui_Dialog):
def __init__(self, parent=None):
super(SettingsUI, self).__init__()
self.setupUi(self)
self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
self._translate = QtCore.QCoreApplication.translate
self.parent = parent
self.database_path = self.parent.database_path
self.resize(self.parent.settings['settings_dialog_size'])
self.move(self.parent.settings['settings_dialog_position'])
install_dir = os.path.realpath(__file__)
install_dir = pathlib.Path(install_dir).parents[1]
aboutfile_path = os.path.join(install_dir, 'lector', 'resources', 'about.html')
with open(aboutfile_path) as about_html:
self.aboutBox.setHtml(about_html.read())
self.paths = None
self.thread = None
self.filesystem_model = None
self.tag_data_copy = None
english_string = self._translate('SettingsUI', 'English')
spanish_string = self._translate('SettingsUI', 'Spanish')
hindi_string = self._translate('SettingsUI', 'Hindi')
languages = [english_string, spanish_string, hindi_string]
self.languageBox.addItems(languages)
current_language = self.parent.settings['dictionary_language']
if current_language == 'en':
self.languageBox.setCurrentIndex(0)
elif current_language == 'es':
self.languageBox.setCurrentIndex(1)
else:
self.languageBox.setCurrentIndex(2)
self.languageBox.activated.connect(self.change_dictionary_language)
self.okButton.setToolTip(
self._translate('SettingsUI', 'Save changes and start library scan'))
self.okButton.clicked.connect(self.start_library_scan)
self.cancelButton.clicked.connect(self.cancel_pressed)
# Radio buttons
if self.parent.settings['icon_theme'] == 'DarkIcons':
self.darkIconsRadio.setChecked(True)
else:
self.lightIconsRadio.setChecked(True)
self.darkIconsRadio.clicked.connect(self.change_icon_theme)
self.lightIconsRadio.clicked.connect(self.change_icon_theme)
# Check boxes
self.autoTags.setChecked(self.parent.settings['auto_tags'])
self.coverShadows.setChecked(self.parent.settings['cover_shadows'])
self.refreshLibrary.setChecked(self.parent.settings['scan_library'])
self.fileRemember.setChecked(self.parent.settings['remember_files'])
self.performCulling.setChecked(self.parent.settings['perform_culling'])
self.cachingEnabled.setChecked(self.parent.settings['caching_enabled'])
self.hideScrollBars.setChecked(self.parent.settings['hide_scrollbars'])
self.scrollSpeedSlider.setValue(self.parent.settings['scroll_speed'])
self.readAtPercent.setValue(self.parent.settings['consider_read_at'])
self.autoTags.clicked.connect(self.manage_checkboxes)
self.coverShadows.clicked.connect(self.manage_checkboxes)
self.refreshLibrary.clicked.connect(self.manage_checkboxes)
self.fileRemember.clicked.connect(self.manage_checkboxes)
self.performCulling.clicked.connect(self.manage_checkboxes)
self.cachingEnabled.clicked.connect(self.manage_checkboxes)
self.hideScrollBars.clicked.connect(self.manage_checkboxes)
self.scrollSpeedSlider.valueChanged.connect(self.change_scroll_speed)
self.readAtPercent.valueChanged.connect(self.change_read_at)
# Generate the QStandardItemModel for the listView
self.listModel = QtGui.QStandardItemModel()
library_string = self._translate('SettingsUI', 'Library')
switches_string = self._translate('SettingsUI', 'Switches')
annotations_string = self._translate('SettingsUI', 'Annotations')
about_string = self._translate('SettingsUI', 'About')
list_options = [
library_string, switches_string, annotations_string, about_string]
for i in list_options:
item = QtGui.QStandardItem()
item.setText(i)
self.listModel.appendRow(item)
self.listView.setModel(self.listModel)
self.listView.clicked.connect(self.page_switch)
# Generate annotation settings
self.annotations_list = []
self.generate_annotations()
# Init colors
self.default_stylesheet = self.foregroundCheck.styleSheet()
self.foreground = QtGui.QColor.fromRgb(0, 0, 0)
self.highlight = QtGui.QColor.fromRgb(255, 255, 255)
# Connect annotation related checkboxes
self.foregroundCheck.clicked.connect(self.format_checkboxes)
self.highlightCheck.clicked.connect(self.format_checkboxes)
self.boldCheck.clicked.connect(self.format_checkboxes)
self.italicCheck.clicked.connect(self.format_checkboxes)
self.underlineCheck.clicked.connect(self.format_checkboxes)
# Generate the filesystem treeView
self.generate_tree()
def generate_tree(self):
# Fetch all directories in the database
paths = database.DatabaseFunctions(
self.database_path).fetch_data(
('Path', 'Name', 'Tags', 'CheckState'),
'directories',
{'Path': ''},
'LIKE')
self.parent.generate_library_filter_menu(paths)
directory_data = {}
if not paths:
print('Database: No paths for settings...')
else:
# Convert to the dictionary format that is
# to be fed into the QFileSystemModel
for i in paths:
directory_data[i[0]] = {
'name': i[1],
'tags': i[2],
'check_state': i[3]}
self.filesystem_model = MostExcellentFileSystemModel(directory_data)
self.filesystem_model.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Dirs)
self.treeView.setModel(self.filesystem_model)
# TODO
# This here might break on them pestilent non unixy OSes
# Check and see
root_directory = QtCore.QDir().rootPath()
self.treeView.setRootIndex(
self.filesystem_model.setRootPath(root_directory))
# Set the treeView and QFileSystemModel to its desired state
selected_paths = [
i for i in directory_data if directory_data[i]['check_state'] == QtCore.Qt.Checked]
expand_paths = set()
for i in selected_paths:
# Recursively grind down parent paths for expansion
this_path = i
while True:
parent_path = os.path.dirname(this_path)
if parent_path == this_path:
break
expand_paths.add(parent_path)
this_path = parent_path
# Expand all the parent paths derived from the selected path
if root_directory in expand_paths:
expand_paths.remove(root_directory)
for i in expand_paths:
this_index = self.filesystem_model.index(i)
self.treeView.expand(this_index)
header_sizes = self.parent.settings['settings_dialog_headers']
if header_sizes:
for count, i in enumerate((0, 4)):
self.treeView.setColumnWidth(i, int(header_sizes[count]))
# TODO
# Set a QSortFilterProxy model on top of the existing QFileSystem model
# self.filesystem_proxy_model = FileSystemProxyModel()
# self.filesystem_proxy_model.setSourceModel(self.filesystem_model)
# self.treeView.setModel(self.filesystem_proxy_model)
for i in range(1, 4):
self.treeView.hideColumn(i)
def start_library_scan(self):
self.hide()
data_pairs = []
for i in self.filesystem_model.tag_data.items():
data_pairs.append([
i[0], i[1]['name'], i[1]['tags'], i[1]['check_state']
])
database.DatabaseFunctions(
self.database_path).set_library_paths(data_pairs)
if not data_pairs:
try:
if self.sender().objectName() == 'reloadLibrary':
self.show()
except AttributeError:
pass
database.DatabaseFunctions(
self.database_path).delete_from_database('*', '*')
self.parent.lib_ref.generate_model('build')
self.parent.lib_ref.generate_proxymodels()
self.parent.generate_library_filter_menu()
return
# Update the main window library filter menu
self.parent.generate_library_filter_menu(data_pairs)
self.parent.set_library_filter()
# Disallow rechecking until the first check completes
self.okButton.setEnabled(False)
self.parent.libraryToolBar.reloadLibraryButton.setEnabled(False)
self.okButton.setToolTip(
self._translate('SettingsUI', 'Library scan in progress...'))
# Traverse directories looking for files
self.parent.statusMessage.setText(
self._translate('SettingsUI', 'Checking library folders'))
self.thread = BackGroundBookSearch(data_pairs)
self.thread.finished.connect(self.finished_iterating)
self.thread.start()
def finished_iterating(self):
# The books the search thread has found
# are now in self.thread.valid_files
if not self.thread.valid_files:
self.parent.move_on()
return
# Hey, messaging is important, okay?
self.parent.statusBar.setVisible(True)
self.parent.sorterProgress.setVisible(True)
self.parent.statusMessage.setText(
self._translate('SettingsUI', 'Parsing files'))
# We now create a new thread to put those files into the database
self.thread = BackGroundBookAddition(
self.thread.valid_files, self.database_path, 'automatic', self.parent)
self.thread.finished.connect(self.parent.move_on)
self.thread.start()
def page_switch(self, index):
self.stackedWidget.setCurrentIndex(index.row())
if index.row() == 0:
self.okButton.setVisible(True)
else:
self.okButton.setVisible(False)
def cancel_pressed(self):
self.filesystem_model.tag_data = copy.deepcopy(self.tag_data_copy)
self.hide()
def hideEvent(self, event):
self.no_more_settings()
event.accept()
def showEvent(self, event):
self.format_preview()
self.tag_data_copy = copy.deepcopy(self.filesystem_model.tag_data)
event.accept()
def no_more_settings(self):
self.parent.libraryToolBar.settingsButton.setChecked(False)
self.resizeEvent()
def resizeEvent(self, event=None):
self.parent.settings['settings_dialog_size'] = self.size()
self.parent.settings['settings_dialog_position'] = self.pos()
table_headers = []
for i in [0, 4]:
table_headers.append(self.treeView.columnWidth(i))
self.parent.settings['settings_dialog_headers'] = table_headers
def change_icon_theme(self):
if self.sender() == self.darkIconsRadio:
self.parent.settings['icon_theme'] = 'DarkIcons'
else:
self.parent.settings['icon_theme'] = 'LightIcons'
def change_dictionary_language(self, event):
language_dict = {
0: 'en',
1: 'es',
2: 'hi'}
self.parent.settings['dictionary_language'] = language_dict[self.languageBox.currentIndex()]
def change_scroll_speed(self, event=None):
self.parent.settings['scroll_speed'] = self.scrollSpeedSlider.value()
def change_read_at(self, event=None):
self.parent.settings['consider_read_at'] = self.readAtPercent.value()
def manage_checkboxes(self, event=None):
sender = self.sender().objectName()
sender_dict = {
'coverShadows': 'cover_shadows',
'autoTags': 'auto_tags',
'refreshLibrary': 'scan_library',
'fileRemember': 'remember_files',
'performCulling': 'perform_culling',
'cachingEnabled': 'caching_enabled',
'hideScrollBars': 'hide_scrollbars'}
self.parent.settings[sender_dict[sender]] = not self.parent.settings[sender_dict[sender]]
if not self.performCulling.isChecked():
self.parent.cover_functions.load_all_covers()
def generate_annotations(self):
annotations_model = QtGui.QStandardItemModel()
items_list = ['Sample annotation']
for i in items_list:
# TODO
# Set annotation types to user roles
item = QtGui.QStandardItem()
item.setText(i)
annotations_model.appendRow(item)
self.annotationsList.setModel(annotations_model)
def format_preview(self):
self.previewView.setText('Vidistine nuper imagines moventes bonas?')
profile_index = self.parent.bookToolBar.profileBox.currentIndex()
current_profile = self.parent.bookToolBar.profileBox.itemData(
profile_index, QtCore.Qt.UserRole)
if not current_profile:
return
font = current_profile['font']
self.foreground = current_profile['foreground']
background = current_profile['background']
font_size = current_profile['font_size']
self.previewView.setStyleSheet(
"QTextEdit {{font-family: {0}; font-size: {1}px; color: {2}; background-color: {3}}}".format(
font, font_size, self.foreground.name(), background.name()))
block_format = QtGui.QTextBlockFormat()
block_format.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)
cursor = self.previewView.textCursor()
while True:
old_position = cursor.position()
cursor.mergeBlockFormat(block_format)
cursor.movePosition(QtGui.QTextCursor.NextBlock, 0, 1)
new_position = cursor.position()
if old_position == new_position:
break
def update_preview(self):
cursor = self.previewView.textCursor()
cursor.setPosition(0)
cursor.movePosition(QtGui.QTextCursor.End, QtGui.QTextCursor.KeepAnchor)
# TODO
# Other kinds of text markup
previewCharFormat = QtGui.QTextCharFormat()
if self.foregroundCheck.isChecked():
previewCharFormat.setForeground(self.foreground)
highlight = QtCore.Qt.transparent
if self.highlightCheck.isChecked():
highlight = self.highlight
previewCharFormat.setBackground(highlight)
font_weight = QtGui.QFont.Normal
if self.boldCheck.isChecked():
font_weight = QtGui.QFont.Bold
previewCharFormat.setFontWeight(font_weight)
font_italic = False
if self.italicCheck.isChecked():
font_italic = True
previewCharFormat.setFontItalic(font_italic)
font_underline = False
if self.underlineCheck.isChecked():
font_underline = True
previewCharFormat.setFontUnderline(font_underline)
previewCharFormat.setFontStyleStrategy(
QtGui.QFont.PreferAntialias)
cursor.setCharFormat(previewCharFormat)
cursor.clearSelection()
self.previewView.setTextCursor(cursor)
def format_checkboxes(self):
sender = self.sender()
if not sender.isChecked():
sender.setStyleSheet(self.default_stylesheet)
self.update_preview()
return
if sender == self.foregroundCheck:
new_color = self.get_color(self.foreground)
self.foregroundCheck.setStyleSheet(
"QCheckBox {{color: {0}; border: none;}}".format(new_color.name()))
self.foreground = new_color
if sender == self.highlightCheck:
new_color = self.get_color(self.highlight)
self.highlightCheck.setStyleSheet(
"QCheckBox {{background-color: {0}}}".format(new_color.name()))
self.highlight = new_color
# TODO
# See if QCheckboxes support this
if sender == self.boldCheck:
self.boldCheck.setStyleSheet(
"QCheckBox {{font-weight: bold;}}")
self.update_preview()
def get_color(self, current_color):
color_dialog = QtWidgets.QColorDialog()
new_color = color_dialog.getColor(current_color)
if new_color.isValid(): # Returned in case cancel is pressed
return new_color
else:
return current_color