Source code for sas.qtgui.MainWindow.CategoryManager
import json
import os
from PyQt5 import QtGui
from PyQt5 import QtCore
from PyQt5 import QtWidgets
from collections import defaultdict
from sas.qtgui.Utilities.CategoryInstaller import CategoryInstaller
from sasmodels.sasview_model import load_standard_models
from .UI.CategoryManagerUI import Ui_CategoryManagerUI
from .UI.ChangeCategoryUI import Ui_ChangeCategoryUI
[docs]class Categories(object):
"""
Container class for accessing model categories
"""
[docs] def __init__(self):
self.master_category_dict = defaultdict(list)
self.by_model_dict = defaultdict(list)
self.model_enabled_dict = defaultdict(bool)
self.models = {}
# Prepare the master category list
self.readCategoryInfo()
# Prepare model->category lookup
self.setupModelDict()
[docs] def readCategoryInfo(self):
"""
Reads the categories in from file
"""
categorization_file = CategoryInstaller.get_user_file()
if not os.path.isfile(categorization_file):
categorization_file = CategoryInstaller.get_default_file()
with open(categorization_file, 'rb') as cat_file:
self.master_category_dict = json.load(cat_file)
self.regenerateModelDict()
self.category_list = sorted(self.master_category_dict.keys())
[docs] def saveCategories(self):
"""
Serializes categorization info to file
"""
self.regenerateMasterDict()
with open(CategoryInstaller.get_user_file(), 'w') as cat_file:
json.dump(self.master_category_dict, cat_file)
[docs] def setupModelDict(self):
"""
create a dictionary for model->category mapping
"""
# Load the model dict
models = load_standard_models()
for model in models:
# {model name -> model object}
self.models[model.name] = model
[docs] def regenerateModelDict(self):
"""
Regenerates self.by_model_dict which has each model name as the
key and the list of categories belonging to that model
along with the enabled mapping
"""
self.by_model_dict = defaultdict(list)
for category in self.master_category_dict:
for (model, enabled) in self.master_category_dict[category]:
self.by_model_dict[model].append(category)
self.model_enabled_dict[model] = enabled
[docs] def regenerateMasterDict(self):
"""
regenerates self.master_category_dict from
self.by_model_dict and self.model_enabled_dict
"""
self.master_category_dict = defaultdict(list)
for model in self.by_model_dict:
for category in self.by_model_dict[model]:
self.master_category_dict[category].append\
((model, self.model_enabled_dict[model]))
[docs] def modelToCategory(self):
"""
Getter for the model->category dict
"""
return self.by_model_dict
[docs] def modelDict(self):
"""
Getter for the model list
"""
return self.models
[docs] def categoryDict(self):
"""
Getter for the category dict
"""
return self.master_category_dict
[docs] def categoryList(self):
"""
Getter for the category list
"""
return self.category_list
[docs]class CategoryManager(QtWidgets.QDialog, Ui_CategoryManagerUI):
[docs] def __init__(self, parent=None, manager=None):
super(CategoryManager, self).__init__(parent)
self.setupUi(self)
# disable the context help icon
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
self.communicator = manager.communicator()
self.setWindowTitle("Category Manager")
self.initializeGlobals()
self.initializeModels()
self.initializeSignals()
[docs] def initializeGlobals(self):
"""
Initialize global variables used in this class
"""
# Default checked state
self.chkEnable.setCheckState(QtCore.Qt.Checked)
# Modify is disabled by default
self.cmdModify.setEnabled(False)
# Data for chosen model
self.model_data = None
# Categories object
self.categories = Categories()
[docs] def initializeModels(self):
"""
Set up models and views
"""
# Set the main models
self._category_model = ToolTippedItemModel()
self.lstCategory.setModel(self._category_model)
# Proxy model for showing a subset of model content
self.model_proxy = QtCore.QSortFilterProxyModel(self)
self.model_proxy.setSourceModel(self._category_model)
self.lstCategory.setModel(self.model_proxy)
self.initializeModelList()
self.setTableProperties(self.lstCategory)
self.lstCategory.setAlternatingRowColors(True)
# self.lstCategory.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
# self.lstCategory.setAttribute(QtCore.Qt.WA_MacShowFocusRect, False)
[docs] def initializeModelList(self):
"""
Model category combo setup
"""
self._category_model.clear()
for ind, model in enumerate(self.categories.modelDict()):
item = QtGui.QStandardItem(model)
empty_item = QtGui.QStandardItem()
empty_item.setEditable(False)
# Add a checkbox to it
item.setCheckable(True)
item.setCheckState(QtCore.Qt.Checked)
item.setEditable(False)
current_category = self.categories.modelToCategory()[model]
self._category_model.appendRow([item, empty_item])
self._category_model.item(ind, 1).setText(', '.join(i for i in current_category))
[docs] def initializeSignals(self):
"""
:return:
"""
self.cmdOK.clicked.connect(self.onClose)
self.cmdModify.clicked.connect(self.onModify)
self.cmdReset.clicked.connect(self.onReset)
self.chkEnable.toggled.connect(self.onEnableAll)
# every change in txtSearch
self.txtSearch.textChanged.connect(self.onSearch)
# Signals from the list
selectionModel = self.lstCategory.selectionModel()
selectionModel.selectionChanged.connect(self.onListSelection)
[docs] def onClose(self):
"""
Save the category file before exiting
"""
self.categories.saveCategories()
# Ask the fitting widget to reload the comboboxes
self.communicator.updateModelCategoriesSignal.emit()
self.close()
[docs] def selectedModels(self):
"""
Returns a list of selected models
"""
selected_models = []
selectionModel = self.lstCategory.selectionModel()
selectedRows = selectionModel.selectedRows()
for row in selectedRows:
model_index = self.model_proxy.mapToSource(row)
current_text = self._category_model.itemFromIndex(model_index).text()
selected_models.append(self.categories.modelDict()[current_text])
return selected_models
[docs] def onListSelection(self):
"""
Respond to row selection and update GUI
"""
selected_items = self.selectedModels()
self.cmdModify.setEnabled(len(selected_items) == 1)
[docs] def onReset(self):
"""
Reload the saved categories
"""
self.initializeGlobals()
# Reload the Categories object
self.categories = Categories()
# Reload the model
self.initializeModelList()
self.setTableProperties(self.lstCategory)
self.lstCategory.setAlternatingRowColors(True)
[docs] def onEnableAll(self, isChecked):
"""
Respond to the Enable/Disable All checkbox
"""
select = QtCore.Qt.Checked if isChecked else QtCore.Qt.Unchecked
for row in range(self._category_model.rowCount()):
self._category_model.item(row).setCheckState(select)
[docs] def onSearch(self):
"""
Respond to text entered in search field
"""
if self.txtSearch.isModified():
input_to_check = str(self.txtSearch.text())
# redefine the proxy model
self.model_proxy.setFilterRegExp(QtCore.QRegExp(input_to_check,
QtCore.Qt.CaseInsensitive, QtCore.QRegExp.FixedString))
[docs] def onModify(self):
"""
Show the Change Category dialog - modal
"""
# Selected category:
selected_models = self.selectedModels()
if len(selected_models) > 1:
# Somehow we got more than one model - complain!
raise AttributeError("Please select only one model.")
change_dialog = ChangeCategory(parent=self, categories=self.categories, model=selected_models[0])
if change_dialog.exec_() != QtWidgets.QDialog.Accepted:
return
# Reload the model
self.initializeModelList()
self.setTableProperties(self.lstCategory)
self.lstCategory.setAlternatingRowColors(True)
[docs] def setTableProperties(self, table):
"""
Setting table properties
"""
# Table properties
table.verticalHeader().setVisible(False)
table.setAlternatingRowColors(True)
#table.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
table.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
table.resizeColumnsToContents()
# Header
header = table.horizontalHeader()
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.ResizeMode(QtWidgets.QHeaderView.Interactive)
self._category_model.setHeaderData(0, QtCore.Qt.Horizontal, "Model")
self._category_model.setHeaderData(1, QtCore.Qt.Horizontal, "Category")
self._category_model.header_tooltips = ['Select model',
'Change or create new category']
[docs]class ChangeCategory(QtWidgets.QDialog, Ui_ChangeCategoryUI):
"""
Dialog for adding/removing categories for a single model
"""
[docs] def __init__(self, parent=None, categories=None, model=None):
super(ChangeCategory, self).__init__(parent)
self.setupUi(self)
# disable the context help icon
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
self.model = model
self.parent = parent
self.categories = categories
self.initializeElements()
self.initializeList()
self.initializeSignals()
[docs] def initializeElements(self):
"""
Initialize local GUI elements with information from the Categories object
"""
self.cbCategories.addItems(self.categories.categoryList())
self.setWindowTitle("Change Category for: "+ self.model.name)
self.lblTitle.setText("Current categories for " + self.model.name)
self.rbExisting.setChecked(True)
self.onAddChoice()
[docs] def initializeList(self):
"""
Initialize the category list for the given model
"""
current_categories = self.categories.modelToCategory()[self.model.name]
for cat in current_categories:
self.lstCategories.addItem(cat)
[docs] def initializeSignals(self):
"""
Initialize signals for UI elements
"""
self.cmdAdd.clicked.connect(self.onAdd)
self.cmdRemove.clicked.connect(self.onRemove)
self.cmdOK.clicked.connect(self.onOK)
self.cmdCancel.clicked.connect(self.close)
# Signals from the list
self.lstCategories.itemSelectionChanged.connect(self.onListSelection)
# Signals from the radio buttons
self.rbExisting.toggled.connect(self.onAddChoice)
[docs] def onAddChoice(self):
"""
Respond to the type selection for new category
"""
isNew = self.rbNew.isChecked()
self.cbCategories.setEnabled(not isNew)
self.txtNewCategory.setEnabled(isNew)
[docs] def onListSelection(self):
"""
Respond to selection in the category list view
"""
selected_items = self.selectedModels()
self.cmdRemove.setEnabled(len(selected_items) > 0)
[docs] def selectedModels(self):
"""
Returns a list of selected models
"""
selected_categories = []
selectedRows = self.lstCategories.selectedItems()
selected_categories = [str(row.text()) for row in selectedRows]
return selected_categories
[docs] def onAdd(self):
"""
Add the chosen category to the list
"""
if self.rbExisting.isChecked():
new_category = self.cbCategories.currentText()
else:
new_category = self.txtNewCategory.text()
# Display the current value as txt
if new_category:
self.lstCategories.addItem(new_category)
[docs] def onRemove(self):
"""
Remove selected categories in the list
"""
selectedRows = self.lstCategories.selectedItems()
if not selectedRows:
return
for row in selectedRows:
self.lstCategories.takeItem(self.lstCategories.row(row))
[docs] def onOK(self):
"""
Accept the new categories for the model
"""
# Read in the categories
self.categories.modelToCategory()[self.model.name] = self.listCategories()
self.accept()
[docs] def listCategories(self):
"""
Returns the list of categories from the QListWidget
"""
return [str(self.lstCategories.item(i).text()) for i in range(self.lstCategories.count())]