mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Use an icon rather than a color to report errors in line edits. Fixes #2003652 [color frame in edit metadata](https://bugs.launchpad.net/calibre/+bug/2003652)
This commit is contained in:
parent
084cf4e65f
commit
7e67e62c11
@ -1291,10 +1291,6 @@ class Application(QApplication):
|
|||||||
ans.setDevicePixelRatio(device_pixel_ratio)
|
ans.setDevicePixelRatio(device_pixel_ratio)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def stylesheet_for_line_edit(self, is_error=False):
|
|
||||||
col = '#FF2400' if is_error else '#50c878'
|
|
||||||
return f'QLineEdit {{ border: 2px solid {col}; border-radius: 3px }}'
|
|
||||||
|
|
||||||
def _send_file_open_events(self):
|
def _send_file_open_events(self):
|
||||||
with self._file_open_lock:
|
with self._file_open_lock:
|
||||||
if self._file_open_paths:
|
if self._file_open_paths:
|
||||||
|
@ -7,9 +7,8 @@ import regex
|
|||||||
from collections import defaultdict, namedtuple
|
from collections import defaultdict, namedtuple
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from qt.core import (
|
from qt.core import (
|
||||||
QApplication, QComboBox, QCompleter, QDateTime, QDialog, QDialogButtonBox, QFont,
|
QComboBox, QCompleter, QDateTime, QDialog, QDialogButtonBox, QFont, QGridLayout,
|
||||||
QGridLayout, QInputDialog, QLabel, QLineEdit, QProgressBar, QSize, Qt, QVBoxLayout,
|
QInputDialog, QLabel, QLineEdit, QProgressBar, QSize, Qt, QVBoxLayout, pyqtSignal,
|
||||||
pyqtSignal,
|
|
||||||
)
|
)
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
@ -27,7 +26,9 @@ from calibre.gui2.custom_column_widgets import populate_metadata_page
|
|||||||
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
||||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||||
from calibre.gui2.dialogs.template_line_editor import TemplateLineEditor
|
from calibre.gui2.dialogs.template_line_editor import TemplateLineEditor
|
||||||
from calibre.gui2.widgets import LineEditECM
|
from calibre.gui2.widgets import (
|
||||||
|
LineEditECM, setup_status_actions, update_status_actions,
|
||||||
|
)
|
||||||
from calibre.startup import connect_lambda
|
from calibre.startup import connect_lambda
|
||||||
from calibre.utils.config import JSONConfig, dynamic, prefs, tweaks
|
from calibre.utils.config import JSONConfig, dynamic, prefs, tweaks
|
||||||
from calibre.utils.date import internal_iso_format_string, qt_to_dt
|
from calibre.utils.date import internal_iso_format_string, qt_to_dt
|
||||||
@ -499,6 +500,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
def __init__(self, window, rows, model, tab, refresh_books):
|
def __init__(self, window, rows, model, tab, refresh_books):
|
||||||
QDialog.__init__(self, window)
|
QDialog.__init__(self, window)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
setup_status_actions(self.test_result)
|
||||||
self.series.set_sort_func(title_sort)
|
self.series.set_sort_func(title_sort)
|
||||||
self.model = model
|
self.model = model
|
||||||
self.db = model.db
|
self.db = model.db
|
||||||
@ -899,10 +901,11 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
self.s_r_search_field_changed(self.search_field.currentIndex())
|
self.s_r_search_field_changed(self.search_field.currentIndex())
|
||||||
|
|
||||||
def s_r_set_colors(self):
|
def s_r_set_colors(self):
|
||||||
|
tt = ''
|
||||||
if self.s_r_error is not None:
|
if self.s_r_error is not None:
|
||||||
self.test_result.setText(error_message(self.s_r_error))
|
tt = error_message(self.s_r_error)
|
||||||
self.test_result.setStyleSheet(
|
self.test_result.setText(tt)
|
||||||
QApplication.instance().stylesheet_for_line_edit(self.s_r_error is not None))
|
update_status_actions(self.test_result, self.s_r_error is None, tt)
|
||||||
for i in range(0,self.s_r_number_of_books):
|
for i in range(0,self.s_r_number_of_books):
|
||||||
getattr(self, 'book_%d_result'%(i+1)).setText('')
|
getattr(self, 'book_%d_result'%(i+1)).setText('')
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ from calibre.gui2.comments_editor import Editor
|
|||||||
from calibre.gui2.complete2 import EditWithComplete
|
from calibre.gui2.complete2 import EditWithComplete
|
||||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||||
from calibre.gui2.languages import LanguagesEdit as LE
|
from calibre.gui2.languages import LanguagesEdit as LE
|
||||||
from calibre.gui2.widgets import EnLineEdit, FormatList as _FormatList, ImageView
|
from calibre.gui2.widgets import EnLineEdit, FormatList as _FormatList, ImageView, LineEditIndicators
|
||||||
from calibre.gui2.widgets2 import (
|
from calibre.gui2.widgets2 import (
|
||||||
DateTimeEdit, Dialog, RatingEditor, RightClickButton, access_key,
|
DateTimeEdit, Dialog, RatingEditor, RightClickButton, access_key,
|
||||||
populate_standard_spinbox_context_menu,
|
populate_standard_spinbox_context_menu,
|
||||||
@ -261,7 +261,7 @@ class TitleEdit(EnLineEdit, ToMetadataMixin):
|
|||||||
self.dialog = None
|
self.dialog = None
|
||||||
|
|
||||||
|
|
||||||
class TitleSortEdit(TitleEdit, ToMetadataMixin):
|
class TitleSortEdit(TitleEdit, ToMetadataMixin, LineEditIndicators):
|
||||||
|
|
||||||
TITLE_ATTR = FIELD_NAME = 'title_sort'
|
TITLE_ATTR = FIELD_NAME = 'title_sort'
|
||||||
TOOLTIP = _('Specify how this book should be sorted when by title.'
|
TOOLTIP = _('Specify how this book should be sorted when by title.'
|
||||||
@ -270,15 +270,16 @@ class TitleSortEdit(TitleEdit, ToMetadataMixin):
|
|||||||
|
|
||||||
def __init__(self, parent, title_edit, autogen_button, languages_edit):
|
def __init__(self, parent, title_edit, autogen_button, languages_edit):
|
||||||
TitleEdit.__init__(self, parent)
|
TitleEdit.__init__(self, parent)
|
||||||
|
self.setup_status_actions()
|
||||||
self.title_edit = title_edit
|
self.title_edit = title_edit
|
||||||
self.languages_edit = languages_edit
|
self.languages_edit = languages_edit
|
||||||
|
|
||||||
base = self.TOOLTIP
|
base = self.TOOLTIP
|
||||||
ok_tooltip = '<p>' + textwrap.fill(base+'<br><br>' + _(
|
ok_tooltip = '<p>' + textwrap.fill(base+'<br><br>' + _(
|
||||||
' The green color indicates that the current '
|
' The ok icon indicates that the current '
|
||||||
'title sort matches the current title'))
|
'title sort matches the current title'))
|
||||||
bad_tooltip = '<p>'+textwrap.fill(base + '<br><br>' + _(
|
bad_tooltip = '<p>'+textwrap.fill(base + '<br><br>' + _(
|
||||||
' The red color warns that the current '
|
' The error icon warns that the current '
|
||||||
'title sort does not match the current title. '
|
'title sort does not match the current title. '
|
||||||
'No action is required if this is what you want.'))
|
'No action is required if this is what you want.'))
|
||||||
self.tooltips = (ok_tooltip, bad_tooltip)
|
self.tooltips = (ok_tooltip, bad_tooltip)
|
||||||
@ -314,8 +315,8 @@ class TitleSortEdit(TitleEdit, ToMetadataMixin):
|
|||||||
def update_state(self, *args):
|
def update_state(self, *args):
|
||||||
ts = title_sort(self.title_edit.current_val, lang=self.book_lang)
|
ts = title_sort(self.title_edit.current_val, lang=self.book_lang)
|
||||||
normal = ts == self.current_val
|
normal = ts == self.current_val
|
||||||
self.setStyleSheet(QApplication.instance().stylesheet_for_line_edit(not normal))
|
|
||||||
tt = self.tooltips[0 if normal else 1]
|
tt = self.tooltips[0 if normal else 1]
|
||||||
|
self.update_status_actions(normal, tt)
|
||||||
self.setToolTip(tt)
|
self.setToolTip(tt)
|
||||||
self.setWhatsThis(tt)
|
self.setWhatsThis(tt)
|
||||||
|
|
||||||
@ -456,7 +457,7 @@ class AuthorsEdit(EditWithComplete, ToMetadataMixin):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AuthorSortEdit(EnLineEdit, ToMetadataMixin):
|
class AuthorSortEdit(EnLineEdit, ToMetadataMixin, LineEditIndicators):
|
||||||
|
|
||||||
TOOLTIP = _('Specify how the author(s) of this book should be sorted. '
|
TOOLTIP = _('Specify how the author(s) of this book should be sorted. '
|
||||||
'For example Charles Dickens should be sorted as Dickens, '
|
'For example Charles Dickens should be sorted as Dickens, '
|
||||||
@ -470,15 +471,16 @@ class AuthorSortEdit(EnLineEdit, ToMetadataMixin):
|
|||||||
def __init__(self, parent, authors_edit, autogen_button, db,
|
def __init__(self, parent, authors_edit, autogen_button, db,
|
||||||
copy_a_to_as_action, copy_as_to_a_action, a_to_as, as_to_a):
|
copy_a_to_as_action, copy_as_to_a_action, a_to_as, as_to_a):
|
||||||
EnLineEdit.__init__(self, parent)
|
EnLineEdit.__init__(self, parent)
|
||||||
|
self.setup_status_actions()
|
||||||
self.authors_edit = authors_edit
|
self.authors_edit = authors_edit
|
||||||
self.db = db
|
self.db = db
|
||||||
|
|
||||||
base = self.TOOLTIP
|
base = self.TOOLTIP
|
||||||
ok_tooltip = '<p>' + textwrap.fill(base+'<br><br>' + _(
|
ok_tooltip = '<p>' + textwrap.fill(base+'<br><br>' + _(
|
||||||
' The green color indicates that the current '
|
' The ok icon indicates that the current '
|
||||||
'author sort matches the current author'))
|
'author sort matches the current author'))
|
||||||
bad_tooltip = '<p>'+textwrap.fill(base + '<br><br>'+ _(
|
bad_tooltip = '<p>'+textwrap.fill(base + '<br><br>'+ _(
|
||||||
' The red color indicates that the current '
|
' The error icon indicates that the current '
|
||||||
'author sort does not match the current author. '
|
'author sort does not match the current author. '
|
||||||
'No action is required if this is what you want.'))
|
'No action is required if this is what you want.'))
|
||||||
self.tooltips = (ok_tooltip, bad_tooltip)
|
self.tooltips = (ok_tooltip, bad_tooltip)
|
||||||
@ -529,8 +531,8 @@ class AuthorSortEdit(EnLineEdit, ToMetadataMixin):
|
|||||||
au = self.author_sort_from_authors(string_to_authors(au))
|
au = self.author_sort_from_authors(string_to_authors(au))
|
||||||
|
|
||||||
normal = au == self.current_val
|
normal = au == self.current_val
|
||||||
self.setStyleSheet(QApplication.instance().stylesheet_for_line_edit(not normal))
|
|
||||||
tt = self.tooltips[0 if normal else 1]
|
tt = self.tooltips[0 if normal else 1]
|
||||||
|
self.update_status_actions(normal, tt)
|
||||||
self.setToolTip(tt)
|
self.setToolTip(tt)
|
||||||
self.setWhatsThis(tt)
|
self.setWhatsThis(tt)
|
||||||
|
|
||||||
@ -1572,7 +1574,7 @@ class Identifiers(Dialog):
|
|||||||
Dialog.accept(self)
|
Dialog.accept(self)
|
||||||
|
|
||||||
|
|
||||||
class IdentifiersEdit(QLineEdit, ToMetadataMixin):
|
class IdentifiersEdit(QLineEdit, ToMetadataMixin, LineEditIndicators):
|
||||||
LABEL = _('&Ids:')
|
LABEL = _('&Ids:')
|
||||||
BASE_TT = _('Edit the identifiers for this book. '
|
BASE_TT = _('Edit the identifiers for this book. '
|
||||||
'For example: \n\n%s\n\nIf an identifier value contains a comma, you can use the | character to represent it.')%(
|
'For example: \n\n%s\n\nIf an identifier value contains a comma, you can use the | character to represent it.')%(
|
||||||
@ -1582,6 +1584,7 @@ class IdentifiersEdit(QLineEdit, ToMetadataMixin):
|
|||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QLineEdit.__init__(self, parent)
|
QLineEdit.__init__(self, parent)
|
||||||
|
self.setup_status_actions()
|
||||||
self.pat = re.compile(r'[^0-9a-zA-Z]')
|
self.pat = re.compile(r'[^0-9a-zA-Z]')
|
||||||
self.textChanged.connect(self.validate)
|
self.textChanged.connect(self.validate)
|
||||||
self.textChanged.connect(self.data_changed)
|
self.textChanged.connect(self.data_changed)
|
||||||
@ -1652,16 +1655,17 @@ class IdentifiersEdit(QLineEdit, ToMetadataMixin):
|
|||||||
isbn = identifiers.get('isbn', '')
|
isbn = identifiers.get('isbn', '')
|
||||||
tt = self.BASE_TT
|
tt = self.BASE_TT
|
||||||
extra = ''
|
extra = ''
|
||||||
|
ok = None
|
||||||
if not isbn:
|
if not isbn:
|
||||||
sheet = ''
|
pass
|
||||||
elif check_isbn(isbn) is not None:
|
elif check_isbn(isbn) is not None:
|
||||||
sheet = QApplication.instance().stylesheet_for_line_edit()
|
ok = True
|
||||||
extra = '\n\n'+_('This ISBN is valid')
|
extra = '\n\n'+_('This ISBN is valid')
|
||||||
else:
|
else:
|
||||||
sheet = QApplication.instance().stylesheet_for_line_edit(True)
|
ok = False
|
||||||
extra = '\n\n' + _('This ISBN is invalid')
|
extra = '\n\n' + _('This ISBN is invalid')
|
||||||
self.setToolTip(tt+extra)
|
self.setToolTip(tt+extra)
|
||||||
self.setStyleSheet(sheet)
|
self.update_status_actions(ok, self.toolTip())
|
||||||
|
|
||||||
def paste_identifier(self):
|
def paste_identifier(self):
|
||||||
identifier_found = self.parse_clipboard_for_identifier()
|
identifier_found = self.parse_clipboard_for_identifier()
|
||||||
@ -1751,6 +1755,9 @@ class IdentifiersEdit(QLineEdit, ToMetadataMixin):
|
|||||||
return False
|
return False
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
class IndicatorLineEdit(QLineEdit, LineEditIndicators):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ISBNDialog(QDialog): # {{{
|
class ISBNDialog(QDialog): # {{{
|
||||||
|
|
||||||
@ -1763,7 +1770,8 @@ class ISBNDialog(QDialog): # {{{
|
|||||||
l.addWidget(w, 0, 0, 1, 2)
|
l.addWidget(w, 0, 0, 1, 2)
|
||||||
w = QLabel(_('ISBN:'))
|
w = QLabel(_('ISBN:'))
|
||||||
l.addWidget(w, 1, 0, 1, 1)
|
l.addWidget(w, 1, 0, 1, 1)
|
||||||
self.line_edit = w = QLineEdit()
|
self.line_edit = w = IndicatorLineEdit()
|
||||||
|
w.setup_status_actions()
|
||||||
w.setText(txt)
|
w.setText(txt)
|
||||||
w.selectAll()
|
w.selectAll()
|
||||||
w.textChanged.connect(self.checkText)
|
w.textChanged.connect(self.checkText)
|
||||||
@ -1787,17 +1795,17 @@ class ISBNDialog(QDialog): # {{{
|
|||||||
|
|
||||||
def checkText(self, txt):
|
def checkText(self, txt):
|
||||||
isbn = str(txt)
|
isbn = str(txt)
|
||||||
|
ok = None
|
||||||
if not isbn:
|
if not isbn:
|
||||||
sheet = ''
|
pass
|
||||||
extra = ''
|
|
||||||
elif check_isbn(isbn) is not None:
|
elif check_isbn(isbn) is not None:
|
||||||
sheet = QApplication.instance().stylesheet_for_line_edit()
|
|
||||||
extra = _('This ISBN is valid')
|
extra = _('This ISBN is valid')
|
||||||
|
ok = True
|
||||||
else:
|
else:
|
||||||
sheet = QApplication.instance().stylesheet_for_line_edit(True)
|
|
||||||
extra = _('This ISBN is invalid')
|
extra = _('This ISBN is invalid')
|
||||||
|
ok = False
|
||||||
self.line_edit.setToolTip(extra)
|
self.line_edit.setToolTip(extra)
|
||||||
self.line_edit.setStyleSheet(sheet)
|
self.line_edit.update_status_actions(ok, extra)
|
||||||
|
|
||||||
def text(self):
|
def text(self):
|
||||||
return check_isbn(str(self.line_edit.text()))
|
return check_isbn(str(self.line_edit.text()))
|
||||||
|
@ -158,7 +158,6 @@ class SearchBox2(QComboBox): # {{{
|
|||||||
items.append(item)
|
items.append(item)
|
||||||
self.addItems(items)
|
self.addItems(items)
|
||||||
self.line_edit.setPlaceholderText(help_text)
|
self.line_edit.setPlaceholderText(help_text)
|
||||||
self.colorize = colorize
|
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
def clear_history(self):
|
def clear_history(self):
|
||||||
@ -210,10 +209,6 @@ class SearchBox2(QComboBox): # {{{
|
|||||||
self.clear(emit_search=False)
|
self.clear(emit_search=False)
|
||||||
return
|
return
|
||||||
self._in_a_search = ok
|
self._in_a_search = ok
|
||||||
if self.colorize:
|
|
||||||
self.line_edit.setStyleSheet(QApplication.instance().stylesheet_for_line_edit(not ok))
|
|
||||||
else:
|
|
||||||
self.line_edit.setStyleSheet('')
|
|
||||||
|
|
||||||
# Comes from the lineEdit control
|
# Comes from the lineEdit control
|
||||||
def key_pressed(self, event):
|
def key_pressed(self, event):
|
||||||
@ -337,7 +332,7 @@ class SearchBoxMixin: # {{{
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def init_search_box_mixin(self):
|
def init_search_box_mixin(self):
|
||||||
self.search.initialize('main_search_history', colorize=True,
|
self.search.initialize('main_search_history',
|
||||||
help_text=_('Search (For advanced search click the gear icon to the left)'))
|
help_text=_('Search (For advanced search click the gear icon to the left)'))
|
||||||
self.search.cleared.connect(self.search_box_cleared)
|
self.search.cleared.connect(self.search_box_cleared)
|
||||||
# Queued so that search.current_text will be correct
|
# Queued so that search.current_text will be correct
|
||||||
|
@ -546,6 +546,35 @@ class EnLineEdit(LineEditECM, QLineEdit): # {{{
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# LineEditIndicators {{{
|
||||||
|
|
||||||
|
def setup_status_actions(self: QLineEdit):
|
||||||
|
self.status_actions = (
|
||||||
|
self.addAction(QIcon.ic('ok.png'), QLineEdit.ActionPosition.TrailingPosition),
|
||||||
|
self.addAction(QIcon.ic('dialog_error.png'), QLineEdit.ActionPosition.TrailingPosition))
|
||||||
|
self.status_actions[0].setVisible(False)
|
||||||
|
self.status_actions[1].setVisible(False)
|
||||||
|
|
||||||
|
def update_status_actions(self: QLineEdit, ok, tooltip: str = ''):
|
||||||
|
self.status_actions[0].setVisible(bool(ok))
|
||||||
|
self.status_actions[1].setVisible(not ok)
|
||||||
|
if ok:
|
||||||
|
self.status_actions[0].setToolTip(tooltip)
|
||||||
|
elif ok is None:
|
||||||
|
self.status_actions[1].setVisible(False)
|
||||||
|
else:
|
||||||
|
self.status_actions[1].setToolTip(tooltip)
|
||||||
|
|
||||||
|
class LineEditIndicators:
|
||||||
|
|
||||||
|
def setup_status_actions(self):
|
||||||
|
setup_status_actions(self)
|
||||||
|
|
||||||
|
def update_status_actions(self, ok, tooltip=''):
|
||||||
|
update_status_actions(self, ok, tooltip)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class ItemsCompleter(QCompleter): # {{{
|
class ItemsCompleter(QCompleter): # {{{
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
Loading…
x
Reference in New Issue
Block a user