Skip to content

Add support for List2 appendColumn and removeColumn #218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions Lib/vanilla/test/list2/columns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import pprint
import vanilla


def makeItems():
items = []
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
for i, letter in enumerate(letters):
number = i + 1
item = dict(
letter=letter,
number=number
)
items.append(item)
return items


class Test:

def __init__(self):
self.w = vanilla.Window((500, 300))
self.w.line = vanilla.VerticalLine("auto")

columnDescriptions = [
dict(
identifier="letter",
title="Letter",
sortable=True
),
dict(
identifier="number",
title="Number",
sortable=True,
)
]
self.w.list = vanilla.List2(
"auto",
items=makeItems(),
columnDescriptions=columnDescriptions,
)
self.w.appendColumnButton = vanilla.Button(
"auto",
"Append Column",
callback=self.appendColumnButtonCallback
)
self.w.removeColumnButton = vanilla.Button(
"auto",
"Remove Column",
callback=self.removeColumnButtonCallback
)
self.w.getButton = vanilla.Button(
"auto",
"Get Values",
callback=self.getButtonCallback
)
rules = [
"H:|-[list]-|",
"H:[getButton]-[appendColumnButton]-[removeColumnButton]-|",
"V:|-[list]-[appendColumnButton]-|",
"V:|-[list]-[removeColumnButton]-|",
"V:|-[list]-[getButton]-|"
]
self.w.addAutoPosSizeRules(rules)
self.w.open()

def appendColumnButtonCallback(self, sender):
try:
self.w.list.insertColumn(
1,
dict(
identifier="punctuation",
title="Punctuation",
sortable=True,
editable=True
),
)
except Exception as e:
print(e)

def removeColumnButtonCallback(self, sender):
self.w.list.removeColumn(
"punctuation"
)

def getButtonCallback(self, sender):
for i, item in enumerate(self.w.list.get()):
print(f"{i} ----------")
for k, v in item.items():
v = pprint.pformat(v)
print(f"{k} : {v}")
print("\n")


if __name__ == "__main__":
from vanilla.test.testTools import executeVanillaTest
executeVanillaTest(Test)
2 changes: 1 addition & 1 deletion Lib/vanilla/test/testAll.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ def __init__(self, drawGrid=False):
simpleList = List((0, 0, 0, 0), listOptions, enableTypingSensitivity=True)

multiItems = [
{"name": name, "path": os.path.basename(getattr(module, "__file__", "Unknown"))}
{"name": name, "path": os.path.basename(getattr(module, "__file__", None) or "Unknow")}
for name, module in sys.modules.items()
]
columnDescriptions = [
Expand Down
207 changes: 134 additions & 73 deletions Lib/vanilla/vanillaList2.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,40 @@ def setGroupCellClassWithKwargs(self, cls, kwargs):
self._groupRowCellClassKwargs = kwargs

@python_method
def setGetters(self, getters):
self._valueGetters = getters
def addGetter(self, identifier, getter):
self._valueGetters[identifier] = getter

@python_method
def setSetters(self, setters):
self._valueSetters = setters
def removeGetter(self, identifier):
if identifier in self._valueGetters:
del self._valueGetters[identifier]

@python_method
def setValueToCellConverters(self, converters):
self._valueToCellConverters = converters
def addSetter(self, identifier, setter):
self._valueSetters[identifier] = setter

@python_method
def setCellToValueConverters(self, converters):
self._cellToValueConverters = converters
def removeSetter(self, identifier):
if identifier in self._valueGetters:
del self._valueSetters[identifier]

@python_method
def addValueToCellConverters(self, identifier, converter):
self._valueToCellConverters[identifier] = converter

@python_method
def removeValueToCellConverters(self, identifier):
if identifier in self._valueToCellConverters:
del self._valueToCellConverters[identifier]

@python_method
def addCellToValueConverters(self, identifier, converter):
self._cellToValueConverters[identifier] = converter

@python_method
def removeCellToValueConverters(self, identifier):
if identifier in self._cellToValueConverters:
del self._cellToValueConverters[identifier]

@python_method
def vanillaWrapper(self):
Expand Down Expand Up @@ -157,7 +177,7 @@ def getItemValueForColumnAndRow(self, identifier, row):
elif function is not None:
value = function(item)
else:
value = item[identifier]
value = item.get(identifier, "")
if identifier in self._valueToCellConverters:
value = self._valueToCellConverters[identifier](value)
return value
Expand Down Expand Up @@ -638,84 +658,125 @@ def _getDropView(self):
return self._tableView

def _buildColumns(self, columnDescriptions):
getters = {}
setters = {}
cellToValueConverters = {}
valueToCellConverters = {}
rowHeights = []
if osVersionCurrent >= osVersion10_16:
rowHeights.append(24)
self._tableView.setRowHeight_(24)
else:
rowHeights.append(17)
self._tableView.setRowHeight_(17)
for columnDescription in columnDescriptions:
identifier = columnDescription["identifier"]
title = columnDescription.get("title", "")
width = columnDescription.get("width")
minWidth = columnDescription.get("minWidth", width)
maxWidth = columnDescription.get("maxWidth", width)
sortable = columnDescription.get("sortable")
cellClass = columnDescription.get("cellClass", EditTextList2Cell)
cellKwargs = columnDescription.get("cellClassArguments", {})
editable = columnDescription.get("editable", False)
property = columnDescription.get("property")
getMethod = columnDescription.get("getMethod")
setMethod = columnDescription.get("setMethod")
getFunction = columnDescription.get("getFunction")
setFunction = columnDescription.get("setFunction")
cellToValueConverter = columnDescription.get("cellToValueConverter")
valueToCellConverter = columnDescription.get("valueToCellConverter")
getters[identifier] = dict(
self.appendColumn(columnDescription)

def appendColumn(self, columnDescription):
"""
Append a column discription.
The column identifier has to be unique for this table.
"""
self.insertColumn(-1, columnDescription)

def removeColumn(self, identifier):
"""
Remove a column by the `identifier`.
This will fail silently when the column does not exists.
"""
column = self._tableView.tableColumnWithIdentifier_(identifier)
if column:
self._tableView.removeTableColumn_(column)
self._dataSourceAndDelegate.removeGetter(identifier)
self._dataSourceAndDelegate.removeSetter(identifier)
self._dataSourceAndDelegate.removeValueToCellConverters(identifier)
self._dataSourceAndDelegate.removeCellToValueConverters(identifier)

def insertColumn(self, index, columnDescription):
"""
Insert a column discription and an index.
The column identifier has to be unique for this table.
"""
identifier = columnDescription["identifier"]
# dont allow columns with the same identifier
assert not self._tableView.tableColumnWithIdentifier_(identifier), f"Column with identifier '{identifier}' already exists."
title = columnDescription.get("title", "")
width = columnDescription.get("width")
minWidth = columnDescription.get("minWidth", width)
maxWidth = columnDescription.get("maxWidth", width)
sortable = columnDescription.get("sortable")
cellClass = columnDescription.get("cellClass", EditTextList2Cell)
cellKwargs = columnDescription.get("cellClassArguments", {})
editable = columnDescription.get("editable", False)
property = columnDescription.get("property")
getMethod = columnDescription.get("getMethod")
setMethod = columnDescription.get("setMethod")
getFunction = columnDescription.get("getFunction")
setFunction = columnDescription.get("setFunction")
cellToValueConverter = columnDescription.get("cellToValueConverter")
valueToCellConverter = columnDescription.get("valueToCellConverter")

self._dataSourceAndDelegate.addGetter(
identifier,
dict(
property=property,
method=getMethod,
function=getFunction
)
setters[identifier] = dict(
)
self._dataSourceAndDelegate.addSetter(
identifier,
dict(
property=property,
method=setMethod,
function=setFunction
)
if cellToValueConverter is not None:
cellToValueConverters[identifier] = cellToValueConverter
if valueToCellConverter is not None:
valueToCellConverters[identifier] = valueToCellConverter
cellKwargs["editable"] = editable
if editable:
cellKwargs["callback"] = True
self._dataSourceAndDelegate.setCellClassWithKwargsForColumn(
cellClass, cellKwargs, identifier
)

if cellToValueConverter is not None:
self._dataSourceAndDelegate.addCellToValueConverters(
identifier,
cellToValueConverter
)
if valueToCellConverter is not None:
self._dataSourceAndDelegate.addValueToCellConverters(
identifier,
valueToCellConverter
)
if width is not None:
if width == minWidth and width == maxWidth:
resizingMask = AppKit.NSTableColumnNoResizing
else:
resizingMask = AppKit.NSTableColumnUserResizingMask | AppKit.NSTableColumnAutoresizingMask

cellKwargs["editable"] = editable
if editable:
cellKwargs["callback"] = True
self._dataSourceAndDelegate.setCellClassWithKwargsForColumn(
cellClass, cellKwargs, identifier
)
if width is not None:
if width == minWidth and width == maxWidth:
resizingMask = AppKit.NSTableColumnNoResizing
else:
resizingMask = AppKit.NSTableColumnUserResizingMask | AppKit.NSTableColumnAutoresizingMask
column = AppKit.NSTableColumn.alloc().initWithIdentifier_(identifier)
column.setTitle_(title)
column.setResizingMask_(resizingMask)
if width is not None:
column.setWidth_(width)
column.setMinWidth_(minWidth)
column.setMaxWidth_(maxWidth)
if self._allowsSorting and sortable:
sortDescriptor = AppKit.NSSortDescriptor.sortDescriptorWithKey_ascending_selector_(
identifier,
True,
"compare:"
)
column.setSortDescriptorPrototype_(sortDescriptor)
self._tableView.addTableColumn_(column)
# measure the cell to get the row height
cell = cellClass(**cellKwargs)
height = cell._nsObject.fittingSize().height
del cell
rowHeights.append(height)
self._dataSourceAndDelegate.setGetters(getters)
self._dataSourceAndDelegate.setSetters(setters)
self._dataSourceAndDelegate.setCellToValueConverters(cellToValueConverters)
self._dataSourceAndDelegate.setValueToCellConverters(valueToCellConverters)
self._tableView.setRowHeight_(max(rowHeights))
else:
resizingMask = AppKit.NSTableColumnUserResizingMask | AppKit.NSTableColumnAutoresizingMask
column = AppKit.NSTableColumn.alloc().initWithIdentifier_(identifier)
column.setTitle_(title)
column.setResizingMask_(resizingMask)
if width is not None:
column.setWidth_(width)
column.setMinWidth_(minWidth)
column.setMaxWidth_(maxWidth)
if self._allowsSorting and sortable:
sortDescriptor = AppKit.NSSortDescriptor.sortDescriptorWithKey_ascending_selector_(
identifier,
True,
"compare:"
)
column.setSortDescriptorPrototype_(sortDescriptor)
self._tableView.addTableColumn_(column)

if index != -1 and index != len(self._tableView.tableColumns()):
self._tableView.moveColumn_toColumn_(
self._tableView.columnWithIdentifier_(identifier),
index
)
# measure the cell to get the row height
cell = cellClass(**cellKwargs)
height = cell._nsObject.fittingSize().height
del cell
if height > self._tableView.rowHeight():
self._tableView.setRowHeight_(height)

def _wrapItem(self, item):
if isinstance(item, simpleDataTypes):
Expand Down