Use multi-read styles. Table combo box style improvements. Tooltips
This commit is contained in:
parent
770cce5ac0
commit
29dc7ad830
@ -230,7 +230,15 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
|
||||
match = self.current_match()
|
||||
ca = ComicArchive(self.current_match_set.original_path)
|
||||
|
||||
md = ca.read_metadata(self.config.internal__load_data_style)
|
||||
# TODO should this follow the same as CLI: filename (-f), read styles (-t), command line (-m)
|
||||
# TODO Same is used for taggerwindow.py:~1734 Make a method?
|
||||
md = GenericMetadata()
|
||||
try:
|
||||
for style in self.load_data_styles.keys():
|
||||
md.overlay(ca.read_metadata(style))
|
||||
except Exception as e:
|
||||
logger.error("Failed to load metadata for %s: %s", ca.path, e)
|
||||
|
||||
if md.is_empty:
|
||||
md = ca.metadata_from_filename(
|
||||
self.config.Filename_Parsing__filename_parser,
|
||||
@ -265,4 +273,4 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
|
||||
)
|
||||
break
|
||||
|
||||
ca.load_cache(list(metadata_styles))
|
||||
ca.load_cache(list(metadata_styles)) # TODO Should this be what the others do in taggerwindow.py etc.?
|
||||
|
@ -249,12 +249,11 @@ class CLI:
|
||||
|
||||
md.overlay(f_md)
|
||||
|
||||
for style in self.config.Runtime_Options__type:
|
||||
for style in reversed(self.config.Runtime_Options__type):
|
||||
if ca.has_metadata(style):
|
||||
try:
|
||||
t_md = ca.read_metadata(style)
|
||||
md.overlay(t_md)
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error("Failed to load metadata for %s: %s", ca.path, e)
|
||||
|
||||
|
@ -160,14 +160,14 @@ def register_runtime(parser: settngs.Manager) -> None:
|
||||
parser.add_setting(
|
||||
"--json", "-j", action="store_true", help="Output json on stdout. Ignored in interactive mode.", file=False
|
||||
)
|
||||
|
||||
# TODO Break read and write apart?
|
||||
parser.add_setting(
|
||||
"-t",
|
||||
"--type",
|
||||
metavar=f"{{{','.join(metadata_styles).upper()}}}",
|
||||
default=[],
|
||||
type=metadata_type,
|
||||
help="""Specify TYPE as either CR, CBL or COMET\n(as either ComicRack, ComicBookLover,\nor CoMet style tags, respectively).\nUse commas for multiple types.\nFor searching the metadata will use the first listed:\neg '-t cbl,cr' with no CBL tags, CR will be used if they exist\n\n""",
|
||||
help="""Specify TYPE as either CR, CBL or COMET\n(as either ComicRack, ComicBookLover,\nor CoMet style tags, respectively).\nUse commas for multiple types.\nFor searching the metadata will be overlayed in reverse order (hierarchical):\ne.g. '-t cbl,cr' first CR tags are read, then CBL overlayed over the top.\n\n""",
|
||||
file=False,
|
||||
)
|
||||
parser.add_setting(
|
||||
|
@ -32,7 +32,7 @@ def internal(parser: settngs.Manager) -> None:
|
||||
# automatic settings
|
||||
parser.add_setting("install_id", default=uuid.uuid4().hex, cmdline=False)
|
||||
parser.add_setting("save_data_style", default=["cbi"], cmdline=False)
|
||||
parser.add_setting("load_data_style", default="cbi", cmdline=False)
|
||||
parser.add_setting("load_data_style", default=[{"cbi": 0}], cmdline=False)
|
||||
parser.add_setting("last_opened_folder", default="", cmdline=False)
|
||||
parser.add_setting("window_width", default=0, cmdline=False)
|
||||
parser.add_setting("window_height", default=0, cmdline=False)
|
||||
|
@ -41,7 +41,7 @@ class SettngsNS(settngs.TypedNS):
|
||||
|
||||
internal__install_id: str
|
||||
internal__save_data_style: list[str]
|
||||
internal__load_data_style: str
|
||||
internal__load_data_style: dict[str, int]
|
||||
internal__last_opened_folder: str
|
||||
internal__window_width: int
|
||||
internal__window_height: int
|
||||
|
@ -523,6 +523,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
if self.cbxShortMetadataNames.isChecked() != self.config[0].General__use_short_metadata_names:
|
||||
self.config[0].General__use_short_metadata_names = self.cbxShortMetadataNames.isChecked()
|
||||
self.parent().populate_style_names()
|
||||
self.parent().adjust_load_style_combo()
|
||||
self.parent().adjust_save_style_combo()
|
||||
|
||||
self.config[0].Issue_Identifier__series_match_identify_thresh = self.sbNameMatchIdentifyThresh.value()
|
||||
|
@ -214,15 +214,20 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
if config[0].Runtime_Options__type and isinstance(config[0].Runtime_Options__type[0], str):
|
||||
# respect the command line option tag type
|
||||
config[0].internal__save_data_style = config[0].Runtime_Options__type
|
||||
config[0].internal__load_data_style = config[0].Runtime_Options__type[0]
|
||||
# TODO Should the command line have seperate options for read and write?
|
||||
cmd_read_style = {}
|
||||
for i, style in enumerate(config[0].Runtime_Options__type):
|
||||
cmd_read_style.update({style: i})
|
||||
config[0].internal__load_data_style = cmd_read_style
|
||||
|
||||
for style in config[0].internal__save_data_style:
|
||||
if style not in metadata_styles:
|
||||
config[0].internal__save_data_style.remove(style)
|
||||
if config[0].internal__load_data_style not in metadata_styles:
|
||||
config[0].internal__load_data_style = list(metadata_styles.keys())[0]
|
||||
for style in config[0].internal__load_data_style.keys():
|
||||
if style not in metadata_styles:
|
||||
del config[0].internal__load_data_style[style]
|
||||
self.save_data_styles: list[str] = config[0].internal__save_data_style
|
||||
self.load_data_style: str = config[0].internal__load_data_style
|
||||
self.load_data_styles: dict[str, int] = config[0].internal__load_data_style
|
||||
|
||||
self.setAcceptDrops(True)
|
||||
self.view_tag_actions, self.remove_tag_actions = self.tag_actions()
|
||||
@ -271,7 +276,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
self.cbMaturityRating.lineEdit().setAcceptDrops(False)
|
||||
|
||||
# hook up the callbacks
|
||||
self.cbLoadDataStyle.itemChecked.connect(self.set_load_data_style)
|
||||
self.cbLoadDataStyle.itemChanged.connect(self.set_load_data_style)
|
||||
self.cbSaveDataStyle.itemChecked.connect(self.set_save_data_style)
|
||||
self.cbx_sources.currentIndexChanged.connect(self.set_source)
|
||||
self.btnEditCredit.clicked.connect(self.edit_credit)
|
||||
@ -433,7 +438,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
self.actionAutoTag.triggered.connect(self.auto_tag)
|
||||
|
||||
self.actionCopyTags.setShortcut("Ctrl+C")
|
||||
self.actionCopyTags.setStatusTip("Copy one tag style to another")
|
||||
self.actionCopyTags.setStatusTip("Copy first read style tags to enabled modify style(s)")
|
||||
self.actionCopyTags.triggered.connect(self.copy_tags)
|
||||
|
||||
self.actionRemoveAuto.setShortcut("Ctrl+D")
|
||||
@ -1203,24 +1208,25 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
self.update_menus()
|
||||
self.fileSelectionList.update_current_row()
|
||||
|
||||
self.metadata = self.comic_archive.read_metadata(self.load_data_style)
|
||||
self.metadata = GenericMetadata()
|
||||
for style in reversed(self.load_data_styles.keys()):
|
||||
self.metadata.overlay(self.comic_archive.read_metadata(style))
|
||||
self.update_ui_for_archive()
|
||||
else:
|
||||
QtWidgets.QMessageBox.information(self, "Whoops!", "No data to commit!")
|
||||
|
||||
def set_load_data_style(self, s: str) -> None:
|
||||
def set_load_data_style(self) -> None:
|
||||
# TODO Should the message be changed? "manually entered data will be lost" Better desc for all 3 options?
|
||||
# Save, Discard and Cancel - Cancel will keep the data in the form AND change the style. Explain each one?
|
||||
if self.dirty_flag_verification(
|
||||
"Change Tag Read Style", "If you change read tag style now, data in the form will be lost. Are you sure?"
|
||||
"Change Tag Read Style",
|
||||
"If you change read tag style(s) now, data in the form will be lost. Are you sure?",
|
||||
):
|
||||
self.load_data_style = self.cbLoadDataStyle.itemData(s)
|
||||
self.config[0].internal__load_data_style = self.load_data_style
|
||||
self.load_data_styles = self.cbLoadDataStyle.currentData()
|
||||
self.config[0].internal__load_data_style = self.load_data_styles
|
||||
self.update_menus()
|
||||
if self.comic_archive is not None:
|
||||
self.load_archive(self.comic_archive)
|
||||
else:
|
||||
self.cbLoadDataStyle.currentIndexChanged.disconnect(self.set_load_data_style)
|
||||
self.adjust_load_style_combo()
|
||||
self.cbLoadDataStyle.currentIndexChanged.connect(self.set_load_data_style)
|
||||
|
||||
def set_save_data_style(self) -> None:
|
||||
self.save_data_styles = self.cbSaveDataStyle.currentData()
|
||||
@ -1392,28 +1398,36 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
self.cbx_sources.setCurrentIndex(self.cbx_sources.findData(self.config[0].Sources__source))
|
||||
|
||||
def adjust_load_style_combo(self) -> None:
|
||||
# select the current style
|
||||
self.cbLoadDataStyle.setCurrentIndex(self.cbLoadDataStyle.findData(self.load_data_style))
|
||||
# select the enabled styles
|
||||
unchecked = set(metadata_styles.keys()) - set(self.load_data_styles.keys())
|
||||
for style, order in self.load_data_styles.items():
|
||||
self.cbLoadDataStyle.setItemChecked(self.cbLoadDataStyle.findData(style), True, order)
|
||||
for style in unchecked:
|
||||
self.cbLoadDataStyle.setItemChecked(self.cbLoadDataStyle.findData(style), False)
|
||||
|
||||
def adjust_save_style_combo(self) -> None:
|
||||
# select the current style
|
||||
unchecked = set(metadata_styles.keys()) - set(self.save_data_styles)
|
||||
for style in self.save_data_styles:
|
||||
self.cbSaveDataStyle.setItemChecked(self.cbLoadDataStyle.findData(style), True)
|
||||
self.cbSaveDataStyle.setItemChecked(
|
||||
self.cbSaveDataStyle.findData(style), True
|
||||
) # Why were these read style?
|
||||
for style in unchecked:
|
||||
self.cbSaveDataStyle.setItemChecked(self.cbLoadDataStyle.findData(style), False)
|
||||
self.cbSaveDataStyle.setItemChecked(self.cbSaveDataStyle.findData(style), False)
|
||||
self.update_metadata_style_tweaks()
|
||||
|
||||
def populate_style_names(self) -> None:
|
||||
# First clear all entries (called from settingswindow.py)
|
||||
self.cbSaveDataStyle.clear()
|
||||
self.cbLoadDataStyle.emptyTable()
|
||||
# Add the entries to the tag style combobox
|
||||
for style in metadata_styles.values():
|
||||
self.cbLoadDataStyle.addItem(style.name(), style.short_name)
|
||||
if self.config[0].General__use_short_metadata_names:
|
||||
self.cbSaveDataStyle.addItem(style.short_name.upper(), style.short_name)
|
||||
self.cbLoadDataStyle.addItem(style.short_name.upper(), {style.short_name: -1})
|
||||
else:
|
||||
self.cbSaveDataStyle.addItem(style.name(), style.short_name)
|
||||
self.cbLoadDataStyle.addItem(style.name(), {style.short_name: -1})
|
||||
|
||||
def populate_combo_boxes(self) -> None:
|
||||
self.populate_style_names()
|
||||
@ -1604,7 +1618,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
ca_list = self.fileSelectionList.get_selected_archive_list()
|
||||
has_src_count = 0
|
||||
|
||||
src_style = self.load_data_style
|
||||
# TODO: Take first read style for now (make a better system later)
|
||||
src_style, _ = next(iter(self.load_data_styles.items()))
|
||||
dest_styles = self.save_data_styles
|
||||
|
||||
# Remove the read style from the write style
|
||||
@ -1683,7 +1698,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
failed_list.append(ca.path)
|
||||
|
||||
ca.reset_cache()
|
||||
ca.load_cache([self.load_data_style, *self.save_data_styles])
|
||||
# TODO Could result in dupes? Should only be read styles?
|
||||
ca.load_cache([*self.load_data_styles.keys(), *self.save_data_styles])
|
||||
|
||||
prog_dialog.hide()
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
@ -1727,10 +1743,12 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
ii = IssueIdentifier(ca, self.config[0], self.current_talker())
|
||||
|
||||
# read in metadata, and parse file name if not there
|
||||
# TODO should this follow the same as CLI: filename (-f), read styles (-t), command line (-m)
|
||||
md = GenericMetadata()
|
||||
try:
|
||||
md = ca.read_metadata(self.load_data_style)
|
||||
for style in self.load_data_styles.keys():
|
||||
md.overlay(ca.read_metadata(style))
|
||||
except Exception as e:
|
||||
md = GenericMetadata()
|
||||
logger.error("Failed to load metadata for %s: %s", ca.path, e)
|
||||
if md.is_empty:
|
||||
md = ca.metadata_from_filename(
|
||||
@ -1888,7 +1906,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
match_results.write_failures.append(res)
|
||||
|
||||
ca.reset_cache()
|
||||
ca.load_cache([self.load_data_style] + self.save_data_styles)
|
||||
# TODO Only read styles required?
|
||||
ca.load_cache([*self.load_data_styles.keys(), *self.save_data_styles])
|
||||
|
||||
return success, match_results
|
||||
|
||||
@ -1937,7 +1956,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
self.auto_tag_log(f"Auto-Tagging {prog_idx} of {len(ca_list)}\n")
|
||||
self.auto_tag_log(f"{ca.path}\n")
|
||||
try:
|
||||
cover_idx = ca.read_metadata(self.load_data_style).get_cover_page_index_list()[0]
|
||||
cover_idx = ca.read_metadata(list(self.load_data_styles.keys())[0]).get_cover_page_index_list()[0]
|
||||
except Exception as e:
|
||||
cover_idx = 0
|
||||
logger.error("Failed to load metadata for %s: %s", ca.path, e)
|
||||
@ -2132,7 +2151,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
if self.dirty_flag_verification(
|
||||
"File Rename", "If you rename files now, unsaved data in the form will be lost. Are you sure?"
|
||||
):
|
||||
dlg = RenameWindow(self, ca_list, self.load_data_style, self.config, self.talkers)
|
||||
# dlg = RenameWindow(self, ca_list, self.load_data_styles, self.config, self.talkers)
|
||||
dlg = RenameWindow(self, ca_list, "cr", self.config, self.talkers)
|
||||
dlg.setModal(True)
|
||||
if dlg.exec() and self.comic_archive is not None:
|
||||
self.fileSelectionList.update_selected_rows()
|
||||
@ -2151,12 +2171,15 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
|
||||
self.config[0].internal__last_opened_folder = os.path.abspath(os.path.split(comic_archive.path)[0])
|
||||
self.comic_archive = comic_archive
|
||||
|
||||
self.metadata = GenericMetadata()
|
||||
try:
|
||||
self.metadata = self.comic_archive.read_metadata(self.load_data_style)
|
||||
for style, order in reversed(self.load_data_styles.items()):
|
||||
metadata = self.comic_archive.read_metadata(style)
|
||||
self.metadata.overlay(metadata)
|
||||
except Exception as e:
|
||||
logger.error("Failed to load metadata for %s: %s", self.comic_archive.path, e)
|
||||
self.exception(f"Failed to load metadata for {self.comic_archive.path}:\n\n{e}")
|
||||
self.metadata = GenericMetadata()
|
||||
|
||||
self.update_ui_for_archive()
|
||||
|
||||
|
@ -7,6 +7,8 @@ from typing import Any
|
||||
from PyQt5 import QtGui, QtWidgets
|
||||
from PyQt5.QtCore import QEvent, QModelIndex, QRect, Qt, pyqtSignal
|
||||
|
||||
from comictaggerlib.graphics import graphics_path
|
||||
|
||||
|
||||
# Multiselect combobox from: https://gis.stackexchange.com/a/351152 (with custom changes)
|
||||
class CheckableComboBox(QtWidgets.QComboBox):
|
||||
@ -114,6 +116,16 @@ class CheckableComboBox(QtWidgets.QComboBox):
|
||||
self.setItemChecked(index, True)
|
||||
|
||||
|
||||
class CheckBoxStyle(QtWidgets.QProxyStyle):
|
||||
def subElementRect(
|
||||
self, element: QtWidgets.QStyle.SubElement, option: QtWidgets.QStyleOption, widget: QtWidgets.QWidget = None
|
||||
) -> QRect:
|
||||
r = super().subElementRect(element, option, widget)
|
||||
if element == QtWidgets.QStyle.SE_ItemViewItemCheckIndicator:
|
||||
r.moveCenter(option.rect.center())
|
||||
return r
|
||||
|
||||
|
||||
class SortLabelTableWidgetItem(QtWidgets.QTableWidgetItem):
|
||||
"""Custom QTableWidgetItem to sort with '-' below numbers"""
|
||||
|
||||
@ -126,30 +138,30 @@ class SortLabelTableWidgetItem(QtWidgets.QTableWidgetItem):
|
||||
|
||||
|
||||
class HoverQLabel(QtWidgets.QLabel):
|
||||
"""A QLabel with two QButtons that appear on hover"""
|
||||
|
||||
def __init__(self, text: str, parent: TableComboBox):
|
||||
super().__init__(text, parent=parent)
|
||||
self.combobox = parent
|
||||
|
||||
self.button_up = QtWidgets.QPushButton("Up", self)
|
||||
self.button_up = QtWidgets.QPushButton(QtGui.QIcon(str(graphics_path / "up.png")), "", self)
|
||||
self.button_up.clicked.connect(self.button_up_clicked)
|
||||
self.button_up.setToolTip("Move style up in order")
|
||||
self.button_up.hide()
|
||||
|
||||
self.button_down = QtWidgets.QPushButton("Down", self)
|
||||
self.button_down = QtWidgets.QPushButton(QtGui.QIcon(str(graphics_path / "down.png")), "", self)
|
||||
self.button_down.clicked.connect(self.button_down_clicked)
|
||||
self.button_down.setToolTip("Move style down in order")
|
||||
self.button_down.hide()
|
||||
|
||||
# Place 'down' button on left side
|
||||
self.button_down.move(self.width() - self.button_down.width(), 0)
|
||||
self.button_down.resize(self.button_down.sizeHint())
|
||||
|
||||
# Place 'up' button on right side
|
||||
self.button_up.move(self.width() - self.button_up.width() - self.button_down.width(), 0)
|
||||
self.button_up.resize(self.button_up.sizeHint())
|
||||
|
||||
# self.resizeEvent = self.adjustButton
|
||||
|
||||
def _showHideButtons(self, index: QModelIndex) -> None:
|
||||
# TODO Better to iterate over all?
|
||||
# TODO Better to iterate over all? Send in check state too?
|
||||
item = self.combobox.tableWidget.item(index.row(), 1)
|
||||
item_checked = item.checkState()
|
||||
if index.row() != self.combobox.tableWidget.currentRow():
|
||||
@ -164,6 +176,7 @@ class HoverQLabel(QtWidgets.QLabel):
|
||||
self.button_down.hide()
|
||||
|
||||
def enterEvent(self, event: QEvent | None) -> None:
|
||||
# Need to manually set the rest of the row highlighted
|
||||
index: QModelIndex = self.combobox.tableWidget.indexAt(self.pos())
|
||||
self.combobox.tableWidget.selectRow(index.row())
|
||||
|
||||
@ -193,22 +206,35 @@ class HoverQLabel(QtWidgets.QLabel):
|
||||
|
||||
|
||||
class TableComboBox(QtWidgets.QComboBox):
|
||||
itemChecked = pyqtSignal(str, bool)
|
||||
itemChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Longest width of read style label
|
||||
self.longest = 0
|
||||
|
||||
self.tableWidget = QtWidgets.QTableWidget()
|
||||
self.setModel(self.tableWidget.model())
|
||||
self.setView(self.tableWidget)
|
||||
|
||||
centered_checkbox_style = CheckBoxStyle()
|
||||
self.tableWidget.setStyle(centered_checkbox_style)
|
||||
|
||||
self.tableWidget.setColumnCount(3)
|
||||
self.tableWidget.setHorizontalHeaderLabels([" # ", " Enabled ", "Read Style"])
|
||||
self.tableWidget.horizontalHeaderItem(0).setToolTip("Order of overlay operations")
|
||||
self.tableWidget.horizontalHeaderItem(1).setToolTip("Whether the style is enabled or not")
|
||||
self.tableWidget.horizontalHeaderItem(2).setToolTip("Name of the read style")
|
||||
|
||||
self.tableWidget.horizontalHeader().setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
|
||||
self.tableWidget.resizeColumnsToContents()
|
||||
|
||||
self.tableWidget.verticalHeader().setVisible(False)
|
||||
self.tableWidget.setHorizontalHeaderLabels(["Order", "Enabled", "Read Style"])
|
||||
|
||||
self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
self.tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
|
||||
self.tableWidget.setShowGrid(False)
|
||||
self.tableWidget.horizontalHeader().setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
|
||||
self.tableWidget.resizeColumnsToContents()
|
||||
|
||||
# Prevent popup from closing when clicking on an item
|
||||
self.tableWidget.viewport().installEventFilter(self)
|
||||
@ -219,6 +245,9 @@ class TableComboBox(QtWidgets.QComboBox):
|
||||
self.tableWidget.currentCellChanged.connect(self.current_cell_changed)
|
||||
|
||||
def current_cell_changed(self, cur_row: int, cur_col: int, prev_row: int, prev_col: int) -> None:
|
||||
# When rebuilding, cur_row -1 will occur and cause a crash
|
||||
if cur_row == -1:
|
||||
return
|
||||
if prev_row == -1:
|
||||
# First time open
|
||||
cur_index = self.tableWidget.indexFromItem(self.tableWidget.item(cur_row, 0))
|
||||
@ -231,6 +260,42 @@ class TableComboBox(QtWidgets.QComboBox):
|
||||
cur_index = self.tableWidget.indexFromItem(self.tableWidget.item(cur_row, 0))
|
||||
self.tableWidget.cellWidget(cur_row, 2)._showHideButtons(cur_index)
|
||||
|
||||
def _longest_label(self) -> None:
|
||||
# Depending on "short" names for metadata, "Read Style" header or metadata name may be longer
|
||||
style_header_width = 0
|
||||
header_item = self.tableWidget.horizontalHeaderItem(2)
|
||||
if header_item is not None:
|
||||
font_metrics = QtGui.QFontMetrics(header_item.font())
|
||||
style_header_width = font_metrics.width(header_item.text())
|
||||
|
||||
header_width = 0
|
||||
for col in range(self.tableWidget.columnCount() - 1): # Skip "read style" as already done above
|
||||
header_item = self.tableWidget.horizontalHeaderItem(col)
|
||||
if header_item is not None:
|
||||
font_metrics = QtGui.QFontMetrics(header_item.font())
|
||||
text_width = font_metrics.width(header_item.text())
|
||||
header_width += text_width
|
||||
|
||||
# Now check items
|
||||
for i in range(self.count()):
|
||||
hlabel = self.tableWidget.cellWidget(i, 2)
|
||||
hlabel_width = (
|
||||
style_header_width if style_header_width > hlabel.sizeHint().width() else hlabel.sizeHint().width()
|
||||
)
|
||||
# Get sizeHint of one button and double it
|
||||
total_width = hlabel_width + header_width + (hlabel.button_up.sizeHint().width() * 2)
|
||||
|
||||
if total_width > self.longest:
|
||||
self.longest = total_width
|
||||
|
||||
def _resizeTable(self) -> None:
|
||||
self._longest_label()
|
||||
self.tableWidget.setMinimumWidth(self.longest)
|
||||
|
||||
def resizeEvent(self, event: Any | None = None) -> None:
|
||||
super().resizeEvent(event)
|
||||
self._updateText()
|
||||
|
||||
def eventFilter(self, obj: Any, event: Any) -> bool:
|
||||
# Allow events before the combobox list is shown
|
||||
if obj == self.tableWidget.viewport():
|
||||
@ -258,7 +323,12 @@ class TableComboBox(QtWidgets.QComboBox):
|
||||
return True
|
||||
return False
|
||||
|
||||
def emptyTable(self) -> None:
|
||||
self.tableWidget.setRowCount(0)
|
||||
self.longest = 0
|
||||
|
||||
def _move_item(self, index: QModelIndex, up: bool) -> None:
|
||||
"""Move an item up or down in order"""
|
||||
adjust = -1 if up else 1
|
||||
cur_item = self.tableWidget.item(index.row(), 0)
|
||||
cur_item_data = cur_item.data(Qt.UserRole)
|
||||
@ -274,26 +344,40 @@ class TableComboBox(QtWidgets.QComboBox):
|
||||
swap_item.setData(Qt.UserRole, {swap_key: cur_value})
|
||||
|
||||
self._updateLabels()
|
||||
self.itemChanged.emit()
|
||||
# Selected (highlighted) row moves so is no longer under the mouse
|
||||
self.tableWidget.selectRow(index.row())
|
||||
|
||||
def addItem(self, label: str = "-", checked: bool = False, text: str = "", data: Any | None = None) -> None:
|
||||
def addItem(self, text: str = "", data: Any | None = None) -> None:
|
||||
rowPosition = self.tableWidget.rowCount()
|
||||
self.tableWidget.insertRow(rowPosition)
|
||||
|
||||
self.tableWidget.setItem(rowPosition, 0, SortLabelTableWidgetItem(label))
|
||||
sortTblItem = SortLabelTableWidgetItem()
|
||||
sortTblItem.setTextAlignment(Qt.AlignCenter)
|
||||
self.tableWidget.setItem(rowPosition, 0, sortTblItem)
|
||||
|
||||
chkBoxItem = QtWidgets.QTableWidgetItem()
|
||||
chkBoxItem.setCheckState(Qt.Checked if checked else Qt.Unchecked)
|
||||
# Set to true to get around the "one item must be checked" check for setItemChecked
|
||||
chkBoxItem.setCheckState(Qt.Checked)
|
||||
|
||||
self.tableWidget.setItem(rowPosition, 1, chkBoxItem)
|
||||
self.tableWidget.setCellWidget(rowPosition, 2, HoverQLabel(text, parent=self))
|
||||
self.tableWidget.item(rowPosition, 0).setData(Qt.UserRole, data)
|
||||
|
||||
self._updateLabels()
|
||||
self._updateText()
|
||||
# Manual as resizeEvent doesn't trigger
|
||||
self._resizeTable()
|
||||
|
||||
def findData(self, data: str, role: int = Qt.UserRole) -> QModelIndex | None:
|
||||
for i in range(self.count()):
|
||||
item = self.itemData(i)
|
||||
k = list(item.keys())[0]
|
||||
if k == data:
|
||||
return self.tableWidget.indexFromItem(self.tableWidget.item(i, 0))
|
||||
return None
|
||||
|
||||
def currentData(self) -> dict[str, int]:
|
||||
# Return the list of all checked items data
|
||||
res = {}
|
||||
for i in range(self.count()):
|
||||
item = self.tableWidget.item(i, 1)
|
||||
@ -332,7 +416,7 @@ class TableComboBox(QtWidgets.QComboBox):
|
||||
# Disable top up button and bottom down button
|
||||
if val == 1:
|
||||
self.tableWidget.cellWidget(i, 2).button_up.setEnabled(False)
|
||||
# Disable the down button if single item. Show buttons as a sign it is checked
|
||||
# Disable the down button if single item. Show buttons even if disabled to indicate checked
|
||||
if val == cur_data_len:
|
||||
self.tableWidget.cellWidget(i, 2).button_down.setEnabled(False)
|
||||
elif val == cur_data_len:
|
||||
@ -369,33 +453,35 @@ class TableComboBox(QtWidgets.QComboBox):
|
||||
self.setCurrentIndex(-1)
|
||||
self.setPlaceholderText(elidedText)
|
||||
|
||||
def setItemChecked(self, index: QModelIndex, state: bool) -> None:
|
||||
def setItemChecked(self, index: QModelIndex, state: bool, order: int = -1) -> None:
|
||||
if index is None:
|
||||
return
|
||||
qt_state = Qt.Checked if state else Qt.Unchecked
|
||||
item = self.tableWidget.item(index.row(), 1)
|
||||
current = self.currentData()
|
||||
# If we have at least one item checked emit itemChecked with the current check state and update text
|
||||
current_len = len(self.currentData())
|
||||
|
||||
# Require at least one item to be checked and provide a tooltip
|
||||
if len(current) == 1 and not state and item.checkState() == Qt.Checked:
|
||||
if current_len == 1 and not state and item.checkState() == Qt.Checked:
|
||||
QtWidgets.QToolTip.showText(QtGui.QCursor.pos(), self.toolTip(), self, QRect(), 3000)
|
||||
return
|
||||
|
||||
if len(current) > 0:
|
||||
if current_len > 0:
|
||||
item.setCheckState(qt_state)
|
||||
item_data: dict[str, int] = self.itemData(index.row())
|
||||
key_name = list(item_data.keys())[0]
|
||||
if state:
|
||||
next_num = self._nextOrderNumber()
|
||||
data = {key_name: next_num}
|
||||
self.tableWidget.item(index.row(), 0).setText(str(next_num + 1))
|
||||
order_num = order if order != -1 else self._nextOrderNumber()
|
||||
data = {key_name: order_num}
|
||||
self.tableWidget.item(index.row(), 0).setText(str(order_num + 1))
|
||||
self.tableWidget.item(index.row(), 0).setData(Qt.UserRole, data)
|
||||
else:
|
||||
data = {key_name: -1}
|
||||
self.tableWidget.item(index.row(), 0).setText("-")
|
||||
self.tableWidget.item(index.row(), 0).setData(Qt.UserRole, data)
|
||||
# We need to check the order numbers as any number could have been removed
|
||||
# Any number may have been removed so reevaluate all
|
||||
self._setOrderNumbers()
|
||||
|
||||
self.itemChecked.emit(key_name, state)
|
||||
self.itemChanged.emit()
|
||||
self._updateText()
|
||||
self._updateLabels()
|
||||
# Check if buttons need to be shown or hidden
|
||||
|
@ -76,7 +76,11 @@
|
||||
<widget class="QComboBox" name="cbx_sources"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="TableComboBox" name="cbLoadDataStyle"/>
|
||||
<widget class="TableComboBox" name="cbLoadDataStyle">
|
||||
<property name="toolTip">
|
||||
<string>At least one read style must be selected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="CheckableComboBox" name="cbSaveDataStyle"/>
|
||||
|
Loading…
Reference in New Issue
Block a user