%PDF- %PDF-
| Direktori : /lib/calibre/calibre/gui2/library/ |
| Current File : //lib/calibre/calibre/gui2/library/delegates.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import sys
from qt.core import (Qt, QApplication, QStyle, QIcon, QDoubleSpinBox, QStyleOptionViewItem,
QSpinBox, QStyledItemDelegate, QComboBox, QTextDocument, QMenu, QKeySequence,
QAbstractTextDocumentLayout, QFont, QFontInfo, QDate, QDateTimeEdit, QDateTime, QEvent,
QStyleOptionComboBox, QStyleOptionSpinBox, QLocale, QSize, QLineEdit, QDialog, QPalette)
from calibre.ebooks.metadata import rating_to_stars
from calibre.gui2 import UNDEFINED_QDATETIME, rating_font
from calibre.constants import iswindows
from calibre.gui2.widgets import EnLineEdit
from calibre.gui2.widgets2 import populate_standard_spinbox_context_menu, RatingEditor, DateTimeEdit as DateTimeEditBase
from calibre.gui2.complete2 import EditWithComplete
from calibre.utils.date import now, format_date, qt_to_dt, is_date_undefined, internal_iso_format_string
from calibre.utils.config import tweaks
from calibre.utils.icu import sort_key
from calibre.gui2.dialogs.comments_dialog import CommentsDialog, PlainTextDialog
from calibre.gui2.dialogs.tag_editor import TagEditor
from calibre.gui2.languages import LanguagesEdit
class UpdateEditorGeometry:
def updateEditorGeometry(self, editor, option, index):
if editor is None:
return
fm = editor.fontMetrics()
# get the original size of the edit widget
opt = QStyleOptionViewItem(option)
self.initStyleOption(opt, index)
opt.showDecorationSelected = True
opt.decorationSize = QSize(0, 0) # We want the editor to cover the decoration
style = QApplication.style()
initial_geometry = style.subElementRect(QStyle.SubElement.SE_ItemViewItemText, opt, None)
orig_width = initial_geometry.width()
# Compute the required width: the width that can show all of the current value
if hasattr(self, 'get_required_width'):
new_width = self.get_required_width(editor, style, fm)
else:
# The line edit box seems to extend by the space consumed by an 'M'.
# So add that to the text
text = self.displayText(index.data(Qt.ItemDataRole.DisplayRole), QLocale()) + 'M'
srect = style.itemTextRect(fm, editor.geometry(), Qt.AlignmentFlag.AlignLeft, False, text)
new_width = srect.width()
# Now get the size of the combo/spinner arrows and add them to the needed width
if isinstance(editor, (QComboBox, QDateTimeEdit)):
r = style.subControlRect(QStyle.ComplexControl.CC_ComboBox, QStyleOptionComboBox(),
QStyle.SubControl.SC_ComboBoxArrow, editor)
new_width += r.width()
elif isinstance(editor, (QSpinBox, QDoubleSpinBox)):
r = style.subControlRect(QStyle.ComplexControl.CC_SpinBox, QStyleOptionSpinBox(),
QStyle.SubControl.SC_SpinBoxUp, editor)
new_width += r.width()
# Compute the maximum we can show if we consume the entire viewport
pin_view = self.table_widget.pin_view
is_pin_view, p = False, editor.parent()
while p is not None:
if p is pin_view:
is_pin_view = True
break
p = p.parent()
if is_pin_view:
max_width = pin_view.horizontalScrollBar().geometry().width()
else:
view = self.table_widget
max_width = view.horizontalScrollBar().geometry().width() - view.verticalHeader().width()
# What we have to display might not fit. If so, adjust down
new_width = new_width if new_width < max_width else max_width
# See if we need to change the editor's geometry
if new_width <= orig_width:
delta_x = 0
delta_width = 0
else:
# Compute the space available from the left edge of the widget to
# the right edge of the displayed table (the viewport) and the left
# edge of the widget to the left edge of the viewport. These are
# used to position the edit box
space_left = initial_geometry.x()
space_right = max_width - space_left
if editor.layoutDirection() == Qt.LayoutDirection.RightToLeft:
# If language is RtL, align to the cell's right edge if possible
cw = initial_geometry.width()
consume_on_left = min(space_left, new_width - cw)
consume_on_right = max(0, new_width - (consume_on_left + cw))
delta_x = -consume_on_left
delta_width = consume_on_right
else:
# If language is LtR, align to the left if possible
consume_on_right = min(space_right, new_width)
consume_on_left = max(0, new_width - consume_on_right)
delta_x = -consume_on_left
delta_width = consume_on_right - initial_geometry.width()
initial_geometry.adjust(delta_x, 0, delta_width, 0)
editor.setGeometry(initial_geometry)
class DateTimeEdit(DateTimeEditBase): # {{{
def __init__(self, parent, format_):
DateTimeEditBase.__init__(self, parent)
self.setFrame(False)
if format_ == 'iso':
format_ = internal_iso_format_string()
self.setDisplayFormat(format_)
# }}}
# Number Editor {{{
def make_clearing_spinbox(spinbox):
class SpinBox(spinbox):
def contextMenuEvent(self, ev):
m = QMenu(self)
m.addAction(_('Set to undefined') + '\t' + QKeySequence(Qt.Key.Key_Space).toString(QKeySequence.SequenceFormat.NativeText),
self.clear_to_undefined)
m.addSeparator()
populate_standard_spinbox_context_menu(self, m)
m.popup(ev.globalPos())
def clear_to_undefined(self):
self.setValue(self.minimum())
def keyPressEvent(self, ev):
if ev.key() == Qt.Key.Key_Space:
self.clear_to_undefined()
else:
if self.value() == self.minimum():
self.clear()
return spinbox.keyPressEvent(self, ev)
return SpinBox
ClearingSpinBox = make_clearing_spinbox(QSpinBox)
ClearingDoubleSpinBox = make_clearing_spinbox(QDoubleSpinBox)
# }}}
# setter for text-like delegates. Return '' if CTRL is pushed {{{
def check_key_modifier(which_modifier):
v = int(QApplication.keyboardModifiers() & (Qt.KeyboardModifier.ControlModifier + Qt.KeyboardModifier.ShiftModifier))
return v == which_modifier
def get_val_for_textlike_columns(index_):
if check_key_modifier(Qt.KeyboardModifier.ControlModifier):
ct = ''
else:
ct = index_.data(Qt.ItemDataRole.DisplayRole) or ''
return str(ct)
# }}}
class RatingDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
def __init__(self, *args, **kwargs):
QStyledItemDelegate.__init__(self, *args)
self.is_half_star = kwargs.get('is_half_star', False)
self.table_widget = args[0]
self.rf = QFont(rating_font())
self.em = Qt.TextElideMode.ElideMiddle
delta = 0
if iswindows and sys.getwindowsversion().major >= 6:
delta = 2
self.rf.setPointSize(QFontInfo(QApplication.font()).pointSize()+delta)
def get_required_width(self, editor, style, fm):
return editor.sizeHint().width()
def displayText(self, value, locale):
return rating_to_stars(value, self.is_half_star)
def createEditor(self, parent, option, index):
return RatingEditor(parent, is_half_star=self.is_half_star)
def setEditorData(self, editor, index):
if check_key_modifier(Qt.KeyboardModifier.ControlModifier):
val = 0
else:
val = index.data(Qt.ItemDataRole.EditRole)
editor.rating_value = val
def setModelData(self, editor, model, index):
val = editor.rating_value
model.setData(index, val, Qt.ItemDataRole.EditRole)
def sizeHint(self, option, index):
option.font = self.rf
option.textElideMode = self.em
return QStyledItemDelegate.sizeHint(self, option, index)
def paint(self, painter, option, index):
option.font = self.rf
option.textElideMode = self.em
return QStyledItemDelegate.paint(self, painter, option, index)
# }}}
class DateDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
def __init__(self, parent, tweak_name='gui_timestamp_display_format',
default_format='dd MMM yyyy'):
QStyledItemDelegate.__init__(self, parent)
self.table_widget = parent
self.tweak_name = tweak_name
self.format = tweaks[self.tweak_name]
if self.format is None:
self.format = default_format
def displayText(self, val, locale):
d = qt_to_dt(val)
if is_date_undefined(d):
return ''
return format_date(d, self.format)
def createEditor(self, parent, option, index):
return DateTimeEdit(parent, self.format)
def setEditorData(self, editor, index):
if check_key_modifier(Qt.KeyboardModifier.ControlModifier):
val = UNDEFINED_QDATETIME
elif check_key_modifier(Qt.KeyboardModifier.ShiftModifier + Qt.KeyboardModifier.ControlModifier):
val = now()
else:
val = index.data(Qt.ItemDataRole.EditRole)
if is_date_undefined(val):
val = now()
editor.setDateTime(val)
# }}}
class PubDateDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
def __init__(self, *args, **kwargs):
QStyledItemDelegate.__init__(self, *args, **kwargs)
self.format = tweaks['gui_pubdate_display_format']
self.table_widget = args[0]
if self.format is None:
self.format = 'MMM yyyy'
def displayText(self, val, locale):
d = qt_to_dt(val)
if is_date_undefined(d):
return ''
return format_date(d, self.format)
def createEditor(self, parent, option, index):
return DateTimeEdit(parent, self.format)
def setEditorData(self, editor, index):
val = index.data(Qt.ItemDataRole.EditRole)
if check_key_modifier(Qt.KeyboardModifier.ControlModifier):
val = UNDEFINED_QDATETIME
elif check_key_modifier(Qt.KeyboardModifier.ShiftModifier + Qt.KeyboardModifier.ControlModifier):
val = now()
elif is_date_undefined(val):
val = QDate.currentDate()
if isinstance(val, QDateTime):
val = val.date()
editor.setDate(val)
# }}}
class TextDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
def __init__(self, parent):
'''
Delegate for text data. If auto_complete_function needs to return a list
of text items to auto-complete with. If the function is None no
auto-complete will be used.
'''
QStyledItemDelegate.__init__(self, parent)
self.table_widget = parent
self.auto_complete_function = None
def set_auto_complete_function(self, f):
self.auto_complete_function = f
def createEditor(self, parent, option, index):
if self.auto_complete_function:
editor = EditWithComplete(parent)
editor.set_separator(None)
editor.set_clear_button_enabled(False)
complete_items = [i[1] for i in self.auto_complete_function()]
editor.update_items_cache(complete_items)
else:
editor = EnLineEdit(parent)
return editor
def setEditorData(self, editor, index):
editor.setText(get_val_for_textlike_columns(index))
editor.selectAll()
def setModelData(self, editor, model, index):
if isinstance(editor, EditWithComplete):
val = editor.lineEdit().text()
model.setData(index, (val), Qt.ItemDataRole.EditRole)
else:
QStyledItemDelegate.setModelData(self, editor, model, index)
# }}}
class SeriesDelegate(TextDelegate): # {{{
def initStyleOption(self, option, index):
TextDelegate.initStyleOption(self, option, index)
option.textElideMode = Qt.TextElideMode.ElideMiddle
# }}}
class CompleteDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
def __init__(self, parent, sep, items_func_name, space_before_sep=False):
QStyledItemDelegate.__init__(self, parent)
self.sep = sep
self.items_func_name = items_func_name
self.space_before_sep = space_before_sep
self.table_widget = parent
def set_database(self, db):
self.db = db
def createEditor(self, parent, option, index):
if self.db and hasattr(self.db, self.items_func_name):
m = index.model()
col = m.column_map[index.column()]
# If shifted, bring up the tag editor instead of the line editor.
if check_key_modifier(Qt.KeyboardModifier.ShiftModifier) and col != 'authors':
key = col if m.is_custom_column(col) else None
d = TagEditor(parent, self.db, m.id(index.row()), key=key)
if d.exec() == QDialog.DialogCode.Accepted:
m.setData(index, self.sep.join(d.tags), Qt.ItemDataRole.EditRole)
return None
editor = EditWithComplete(parent)
if col == 'tags':
editor.set_elide_mode(Qt.TextElideMode.ElideMiddle)
editor.set_separator(self.sep)
editor.set_clear_button_enabled(False)
editor.set_space_before_sep(self.space_before_sep)
if self.sep == '&':
editor.set_add_separator(tweaks['authors_completer_append_separator'])
if not m.is_custom_column(col):
all_items = getattr(self.db, self.items_func_name)()
else:
all_items = list(self.db.all_custom(
label=self.db.field_metadata.key_to_label(col)))
editor.update_items_cache(all_items)
else:
editor = EnLineEdit(parent)
return editor
def setEditorData(self, editor, index):
editor.setText(get_val_for_textlike_columns(index))
editor.selectAll()
def setModelData(self, editor, model, index):
if isinstance(editor, EditWithComplete):
val = editor.lineEdit().text()
model.setData(index, (val), Qt.ItemDataRole.EditRole)
else:
QStyledItemDelegate.setModelData(self, editor, model, index)
# }}}
class LanguagesDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
def __init__(self, parent):
QStyledItemDelegate.__init__(self, parent)
self.table_widget = parent
def createEditor(self, parent, option, index):
editor = LanguagesEdit(parent=parent)
editor.init_langs(index.model().db)
return editor
def setEditorData(self, editor, index):
editor.show_initial_value(get_val_for_textlike_columns(index))
def setModelData(self, editor, model, index):
val = ','.join(editor.lang_codes)
editor.update_recently_used()
model.setData(index, (val), Qt.ItemDataRole.EditRole)
# }}}
class CcDateDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
'''
Delegate for custom columns dates. Because this delegate stores the
format as an instance variable, a new instance must be created for each
column. This differs from all the other delegates.
'''
def __init__(self, parent):
QStyledItemDelegate.__init__(self, parent)
self.table_widget = parent
def set_format(self, _format):
if not _format:
self.format = 'dd MMM yyyy'
elif _format == 'iso':
self.format = internal_iso_format_string()
else:
self.format = _format
def displayText(self, val, locale):
d = qt_to_dt(val)
if is_date_undefined(d):
return ''
return format_date(d, self.format)
def createEditor(self, parent, option, index):
return DateTimeEdit(parent, self.format)
def setEditorData(self, editor, index):
if check_key_modifier(Qt.KeyboardModifier.ControlModifier):
val = UNDEFINED_QDATETIME
elif check_key_modifier(Qt.KeyboardModifier.ShiftModifier + Qt.KeyboardModifier.ControlModifier):
val = now()
else:
val = index.data(Qt.ItemDataRole.EditRole)
if is_date_undefined(val):
val = now()
editor.setDateTime(val)
def setModelData(self, editor, model, index):
val = editor.dateTime()
if is_date_undefined(val):
val = None
model.setData(index, (val), Qt.ItemDataRole.EditRole)
# }}}
class CcTextDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
'''
Delegate for text data.
'''
def __init__(self, parent):
QStyledItemDelegate.__init__(self, parent)
self.table_widget = parent
def createEditor(self, parent, option, index):
m = index.model()
col = m.column_map[index.column()]
key = m.db.field_metadata.key_to_label(col)
if m.db.field_metadata[col]['datatype'] != 'comments':
editor = EditWithComplete(parent)
editor.set_separator(None)
editor.set_clear_button_enabled(False)
complete_items = sorted(list(m.db.all_custom(label=key)), key=sort_key)
editor.update_items_cache(complete_items)
else:
editor = QLineEdit(parent)
text = index.data(Qt.ItemDataRole.DisplayRole)
if text:
editor.setText(text)
editor.selectAll()
return editor
def setEditorData(self, editor, index):
editor.setText(get_val_for_textlike_columns(index))
editor.selectAll()
def setModelData(self, editor, model, index):
val = editor.text() or ''
if not isinstance(editor, EditWithComplete):
val = val.strip()
model.setData(index, val, Qt.ItemDataRole.EditRole)
# }}}
class CcSeriesDelegate(CcTextDelegate): # {{{
def initStyleOption(self, option, index):
CcTextDelegate.initStyleOption(self, option, index)
option.textElideMode = Qt.TextElideMode.ElideMiddle
# }}}
class CcLongTextDelegate(QStyledItemDelegate): # {{{
'''
Delegate for comments data.
'''
def __init__(self, parent):
QStyledItemDelegate.__init__(self, parent)
self.document = QTextDocument()
def createEditor(self, parent, option, index):
m = index.model()
col = m.column_map[index.column()]
if check_key_modifier(Qt.KeyboardModifier.ControlModifier):
text = ''
else:
text = m.db.data[index.row()][m.custom_columns[col]['rec_index']]
d = PlainTextDialog(parent, text, column_name=m.custom_columns[col]['name'])
if d.exec() == QDialog.DialogCode.Accepted:
m.setData(index, d.text, Qt.ItemDataRole.EditRole)
return None
def setModelData(self, editor, model, index):
model.setData(index, (editor.textbox.html), Qt.ItemDataRole.EditRole)
# }}}
class CcNumberDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
'''
Delegate for text/int/float data.
'''
def __init__(self, parent):
QStyledItemDelegate.__init__(self, parent)
self.table_widget = parent
def createEditor(self, parent, option, index):
m = index.model()
col = m.column_map[index.column()]
if m.custom_columns[col]['datatype'] == 'int':
editor = ClearingSpinBox(parent)
editor.setRange(-1000000, 100000000)
editor.setSpecialValueText(_('Undefined'))
editor.setSingleStep(1)
else:
editor = ClearingDoubleSpinBox(parent)
editor.setSpecialValueText(_('Undefined'))
editor.setRange(-1000000., 100000000.)
editor.setDecimals(2)
return editor
def setModelData(self, editor, model, index):
val = editor.value()
if val == editor.minimum():
val = None
model.setData(index, (val), Qt.ItemDataRole.EditRole)
editor.adjustSize()
def setEditorData(self, editor, index):
m = index.model()
val = m.db.data[index.row()][m.custom_columns[m.column_map[index.column()]]['rec_index']]
if check_key_modifier(Qt.KeyboardModifier.ControlModifier):
val = -1000000
elif val is None:
val = 0
editor.setValue(val)
def get_required_width(self, editor, style, fm):
val = editor.maximum()
text = editor.textFromValue(val)
srect = style.itemTextRect(fm, editor.geometry(), Qt.AlignmentFlag.AlignLeft, False,
text + 'M')
return srect.width()
# }}}
class CcEnumDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
'''
Delegate for text/int/float data.
'''
def __init__(self, parent):
QStyledItemDelegate.__init__(self, parent)
self.table_widget = parent
self.longest_text = ''
def createEditor(self, parent, option, index):
m = index.model()
col = m.column_map[index.column()]
editor = DelegateCB(parent)
editor.addItem('')
max_len = 0
self.longest_text = ''
for v in m.custom_columns[col]['display']['enum_values']:
editor.addItem(v)
if len(v) > max_len:
self.longest_text = v
return editor
def setModelData(self, editor, model, index):
val = str(editor.currentText())
if not val:
val = None
model.setData(index, (val), Qt.ItemDataRole.EditRole)
def get_required_width(self, editor, style, fm):
srect = style.itemTextRect(fm, editor.geometry(), Qt.AlignmentFlag.AlignLeft, False,
self.longest_text + 'M')
return srect.width()
def setEditorData(self, editor, index):
m = index.model()
val = m.db.data[index.row()][m.custom_columns[m.column_map[index.column()]]['rec_index']]
if val is None or check_key_modifier(Qt.KeyboardModifier.ControlModifier):
val = ''
idx = editor.findText(val)
if idx < 0:
editor.setCurrentIndex(0)
else:
editor.setCurrentIndex(idx)
# }}}
class CcCommentsDelegate(QStyledItemDelegate): # {{{
'''
Delegate for comments data.
'''
def __init__(self, parent):
QStyledItemDelegate.__init__(self, parent)
self.document = QTextDocument()
def paint(self, painter, option, index):
self.initStyleOption(option, index)
style = QApplication.style() if option.widget is None \
else option.widget.style()
self.document.setHtml(option.text)
style.drawPrimitive(QStyle.PrimitiveElement.PE_PanelItemViewItem, option, painter, widget=option.widget)
rect = style.subElementRect(QStyle.SubElement.SE_ItemViewItemDecoration, option, self.parent())
ic = option.icon
if rect.isValid() and not ic.isNull():
sz = ic.actualSize(option.decorationSize)
painter.drawPixmap(rect.topLeft(), ic.pixmap(sz))
ctx = QAbstractTextDocumentLayout.PaintContext()
ctx.palette = option.palette
if option.state & QStyle.StateFlag.State_Selected:
ctx.palette.setColor(QPalette.ColorRole.Text, ctx.palette.color(QPalette.ColorRole.HighlightedText))
textRect = style.subElementRect(QStyle.SubElement.SE_ItemViewItemText, option, self.parent())
painter.save()
painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
self.document.documentLayout().draw(painter, ctx)
painter.restore()
def createEditor(self, parent, option, index):
m = index.model()
col = m.column_map[index.column()]
if check_key_modifier(Qt.KeyboardModifier.ControlModifier):
text = ''
else:
text = m.db.data[index.row()][m.custom_columns[col]['rec_index']]
editor = CommentsDialog(parent, text, column_name=m.custom_columns[col]['name'])
d = editor.exec()
if d:
m.setData(index, (editor.textbox.html), Qt.ItemDataRole.EditRole)
return None
def setModelData(self, editor, model, index):
model.setData(index, (editor.textbox.html), Qt.ItemDataRole.EditRole)
# }}}
class DelegateCB(QComboBox): # {{{
def __init__(self, parent):
QComboBox.__init__(self, parent)
def event(self, e):
if e.type() == QEvent.Type.ShortcutOverride:
e.accept()
return QComboBox.event(self, e)
# }}}
class CcBoolDelegate(QStyledItemDelegate, UpdateEditorGeometry): # {{{
def __init__(self, parent):
'''
Delegate for custom_column bool data.
'''
QStyledItemDelegate.__init__(self, parent)
self.table_widget = parent
def createEditor(self, parent, option, index):
editor = DelegateCB(parent)
items = [_('Yes'), _('No'), _('Undefined')]
icons = [I('ok.png'), I('list_remove.png'), I('blank.png')]
if not index.model().db.new_api.pref('bools_are_tristate'):
items = items[:-1]
icons = icons[:-1]
self.longest_text = ''
for icon, text in zip(icons, items):
editor.addItem(QIcon(icon), text)
if len(text) > len(self.longest_text):
self.longest_text = text
return editor
def get_required_width(self, editor, style, fm):
srect = style.itemTextRect(fm, editor.geometry(), Qt.AlignmentFlag.AlignLeft, False,
self.longest_text + 'M')
return srect.width() + editor.iconSize().width()
def setModelData(self, editor, model, index):
val = {0:True, 1:False, 2:None}[editor.currentIndex()]
model.setData(index, (val), Qt.ItemDataRole.EditRole)
def setEditorData(self, editor, index):
m = index.model()
val = m.db.data[index.row()][m.custom_columns[m.column_map[index.column()]]['rec_index']]
if not m.db.new_api.pref('bools_are_tristate'):
val = 1 if not val or check_key_modifier(Qt.KeyboardModifier.ControlModifier) else 0
else:
val = 2 if val is None or check_key_modifier(Qt.KeyboardModifier.ControlModifier) \
else 1 if not val else 0
editor.setCurrentIndex(val)
# }}}
class CcTemplateDelegate(QStyledItemDelegate): # {{{
def __init__(self, parent):
'''
Delegate for custom_column bool data.
'''
QStyledItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
from calibre.gui2.dialogs.template_dialog import TemplateDialog
m = index.model()
mi = m.db.get_metadata(index.row(), index_is_id=False)
if check_key_modifier(Qt.KeyboardModifier.ControlModifier):
text = ''
else:
text = m.custom_columns[m.column_map[index.column()]]['display']['composite_template']
editor = TemplateDialog(parent, text, mi)
editor.setWindowTitle(_("Edit template"))
editor.textbox.setTabChangesFocus(False)
editor.textbox.setTabStopWidth(20)
d = editor.exec()
if d:
m.setData(index, (editor.rule[1]), Qt.ItemDataRole.EditRole)
return None
# }}}