import logging
import re
from typing import List, Union, Optional
from PySide6 import QtGui
from PySide6.QtCore import QAbstractTableModel, Qt, QModelIndex
from cgl.core.config.query import AlchemyConfigManager
CFG = AlchemyConfigManager()
[docs]
class LJItemModel(QAbstractTableModel):
[docs]
def rowCount(self, index):
if self.data_:
return len(self.data_)
else:
return 0
[docs]
def columnCount(self, index):
return len(self.headers)
[docs]
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return None
if role == Qt.DisplayRole:
return self._data[index.row()][index.column()]
return None
[docs]
def clear_data(self):
self.beginResetModel() # Notify views and delegates that model data will be cleared
self._data = [] # Clear the data
self.endResetModel() # Notify views and delegates that model data is cleared
[docs]
class LGListDictionaryItemModel(LJItemModel):
def __init__(self, data):
LJItemModel.__init__(self)
self.headers = []
self.data_ = []
if data:
self.data_ = data
self.keys = data[0].keys()
[docs]
def data(self, index, role):
row = index.row()
key = self.keys[index.column()]
if role == Qt.DisplayRole:
return str(self.data_[row][key])
[docs]
class LGShotgunListDictionaryItemModel(LGListDictionaryItemModel):
def __init__(self, data, display_filter=None):
LJItemModel.__init__(self)
self.headers = []
self.data_ = []
self.keys = []
self.data_filter = False
if data:
self.data_ = data
self.data_filter = True
if display_filter:
for key in display_filter:
self.keys = display_filter.keys()
self.headers.append(display_filter[key])
else:
self.keys = data[0].keys()
for key in self.keys:
self.headers.append(
key.replace("sg_", "").replace("_", " ").title_label()
)
[docs]
def data(self, index, role):
row = index.row()
key = self.keys[index.column()]
if role == Qt.DisplayRole:
try:
data = self.data_[row][key]
if data is None:
return ""
if isinstance(data, dict):
if "name" in data:
return data["name"]
elif "code" in data:
return data["code"]
return str(data)
except KeyError:
return ""
[docs]
class DictionaryItemModel(LJItemModel):
def __init__(self, dict_, header_titles=None):
LJItemModel.__init__(self)
self.data_ = dict_
if header_titles is None:
header_titles = ["key", "value"]
self.headers = header_titles
[docs]
def data(self, index, role):
row = index.row()
col = index.column()
if role == Qt.DisplayRole:
if col == 0:
return self.data_.keys()[row]
elif col == 1:
return self.data_.values()[row]
return
[docs]
class ListItemModel(LJItemModel):
"""
ListItemModel is a custom model for a Qt-based application that inherits from the LJItemModel class.
This model is designed to be used with Qt views (e.g., QTableView, QListView) to display data in a
structured way with an icon in the first column.
"""
def __init__(
self,
data_list: List[List[Union[dict, QtGui.QPixmap, str]]],
header_titles: Optional[List[str]] = None,
data_filter: bool = False,
icon_height: int = 30,
):
"""
Initializes the ListItemModel with the provided data and optional parameters.
Args:
data_list (List[List[Union[dict, QtGui.QPixmap, str]]]): A list of data to be displayed in the view.
header_titles (Optional[List[str]], optional): A list of column headers. Defaults to None.
data_filter (bool, optional): A boolean flag to indicate whether the model should have a filter. Defaults to False.
icon_height (int, optional): The height of the icon to be displayed in the first column. Defaults to 30.
"""
LJItemModel.__init__(self)
if header_titles is None:
header_titles = []
self.data_ = data_list
self.headers = header_titles
self.data_filter = data_filter
self.icon_height = icon_height
[docs]
def columnCount(self, parent=None) -> int:
"""
Returns the number of columns in the model.
Args:
parent (optional): The parent QModelIndex. Defaults to None.
Returns:
int: The number of columns in the model, which is 2 in this case.
"""
return len(self.headers)
[docs]
def data(
self, index: QModelIndex, role: int
) -> Optional[Union[QtGui.QPixmap, str]]:
"""
Provides the appropriate data based on the given QModelIndex `index` and the role `role`.
Args:
index (QModelIndex): The QModelIndex of the data.
role (int): The role of the data (e.g., Qt.DisplayRole, Qt.DecorationRole).
Returns:
Optional[Union[QtGui.QPixmap, str]]: The data to be displayed or decorated, or None if not applicable.
"""
col = index.column()
if col == 1:
return self._handle_icon_column(index, role)
else:
return self._handle_data_column(index, role)
[docs]
def clear_data(self):
print("clearing data")
self.beginResetModel() # Notify views and delegates that model data will be cleared
self.data_ = [] # Clear the data
self.endResetModel() # Notify views and delegates that model data is cleared
def _handle_icon_column(
self, index: QModelIndex, role: int
) -> Optional[QtGui.QPixmap]:
if role == Qt.DecorationRole:
row = index.row()
# icon_path = str(self.data_[row][1])
icon_path = str(self.data_[row][0])
self.icon_pixmap = QtGui.QPixmap(icon_path)
self.scaled_icon_pixmap = self.icon_pixmap.scaledToHeight(
self.icon_height, Qt.SmoothTransformation
)
return self.scaled_icon_pixmap
else:
return None
def _handle_data_column(self, index: QModelIndex, role: int) -> Optional[str]:
row = index.row()
col = index.column()
# check if data is a list of list
# logging.debug(f"data being handled on column is {self.data_}")
data = self.data_[row][col]
if role == Qt.DisplayRole:
return self._format_data_for_display(data)
elif role == Qt.DecorationRole:
if isinstance(data, QtGui.QPixmap):
return data
return None
@staticmethod
def _format_data_for_display(data: Union[dict, QtGui.QPixmap, str]) -> str:
if data is None:
return ""
if isinstance(data, dict):
if "name" in data:
return data["name"]
elif "code" in data:
return data["code"]
if isinstance(data, QtGui.QPixmap):
return ""
return str(data)
@staticmethod
def _format_data_for_display(data: Union[dict, QtGui.QPixmap, str]) -> str:
if data is None:
return ""
if isinstance(data, dict):
if "name" in data:
return data["name"]
elif "code" in data:
return data["code"]
if isinstance(data, QtGui.QPixmap):
return ""
return str(data)
[docs]
class TreeItemModel(LJItemModel):
def __init__(self, data_list, header_titles=None, data_filter=False):
super().__init__()
self.data_ = data_list
self.headers = header_titles
self.data_filter = data_filter
[docs]
def data(self, index, role):
row = index.row()
col = index.column()
if role == Qt.DisplayRole:
try:
data = self.data_[row][col]
if data is None:
return ""
if isinstance(data, dict):
if "name" in data:
return data["name"]
elif "code" in data:
return data["code"]
return str(data)
except KeyError:
return ""
[docs]
class FileTableModel(ListItemModel):
[docs]
def data(self, index, role):
row = index.row()
col = index.column()
if role == Qt.DisplayRole:
return self.data_[row][col]
# if role == Qt.DecorationRole:
# logging.info("Decoration Role", self.data_[row][col])
# keeping this here for reference
# data = self.data_[row][col]
[docs]
class FilesModel(QAbstractTableModel):
def __init__(
self,
data_list,
header_titles=None,
data_filter=False,
path_object=None,
cfg=None,
):
QAbstractTableModel.__init__(self)
# self.setHeaderData(Qt.Horizontal, Qt.AlignLeft, Qt.TextAlignmentRole)
# self.setHeaderData(Qt.Horizontal, Qt.AlignLeft, Qt.TextAlignmentRole)
self.path_object = path_object
self.data_ = data_list
self.headers = header_titles
self.data_filter = data_filter
[docs]
def data(self, index, role):
try:
data = self.data_[index.row()][index.column()]
if role == Qt.DisplayRole:
if data is None:
return ""
elif isinstance(data, dict):
if "name" in data:
return data["name"]
elif "code" in data:
return data["code"]
return data
if role == Qt.DecorationRole:
if "." not in data:
icon_path_ = CFG.get_icon_path("folder24px.png")
return QtGui.QIcon(icon_path_)
if role == Qt.ForegroundRole:
padding_difference = self.has_approved_frame_padding(data)
if padding_difference:
logging.info(
"Padding {} does not match studio padding {}".format(
padding_difference[0], padding_difference[1]
)
)
return QtGui.QColor("red")
except KeyError:
return ""
[docs]
def has_approved_frame_padding(self, filename):
hashes = re.compile("#+")
m = re.search(hashes, filename)
if m:
this_padding = int(len(m.group()))
studio_padding = int(CFG.project_config["default"]["padding"])
if this_padding == studio_padding:
return 0
else:
return [this_padding, studio_padding]
[docs]
def rowCount(self, index):
if self.data_:
return len(self.data_)
else:
return 0
[docs]
def columnCount(self, index):
return len(self.headers) + 1