How can I pad string in such a way, that it aligns? It probably has to do with Character widths…
I think you need to define length before you can pull the maximum value thereof… surprised this doesn’t error out - does it work if you swap the order?
Could you get string character length and then subtract it from an amount of spacing so that the amount of spaces are added based on character length of the string?
The trouble with merely getting string lengths is different characters take up different amounts of space.
“lilililili”
“wowowowowo”
You can’t even just look up a character width as kerning is also a factor. The following are basically the same width:
“NoNoNoNoNo”
“NbNbNbNbNb”
But swap the ‘N’ for a ‘Y’ to get:
“YoYoYoYoYo”
“YbYbYbYbYb”
The kerning puts the ‘o’ beneath the ‘Y’ slightly, making the overall text shorter.
I’m not sure if there are some Python libraries out there you could use to account for all of that, but an easier solution might be to just see how Data-Shapes defines its font and change it for a monospaced one.
I got this far, but I can’t adjust the node to use a different font. I could only find one. "Font = " with Ariel and changed it but it doesn’t seem to have any effect
how?
That’s how I did it first, until I realised String.PadRight can just do it for me.
In Dynamo all characters have the same width. In DataShapes they don’t. This is because of a different font.
If I can somehow change the Font that DataShapes uses, my script will give the desired result
Is there an option for fully justified text? That’d align left and right.
Not exactly what you’re asking but at least the two sides would align with each other.
No I even tried adding a tab instead (\t) but to no avail
Why not use a chart or other display method?
I need the user to select which names are approved for changing.
Would those display methods allow for that?
@Mostafa_El_Ayoubi looks like the latest version of DataShapes has some unused loading of dependencies for the Revit API in the DataTable node - not sure if this is as intended if not but you can likely comment out or remove lines 5-8 and the unused ‘doc’ import on line 13 to make this work in Civil 3D and Sandbox too.
I thought that the DataTable input would work for this, but I guess not.
Some options:
- Live with things not aligning
- Build your own UI in Python and use that instead of Datashapes
- Use two pop-ups, one to show the before, then one to show the after
Hi @RevitRobot
I’ve just written an article on selecting objects (or elements) via a DataTable/DataGrid (in WPF), which you may find useful.
Python Code
__author__ = "Cyril POUPIN"
__license__ = "MIT license"
__version__ = "1.0.1"
import clr
import sys
import System
clr.AddReference('System.Data')
from System.Data import *
clr.AddReference("System.Xml")
clr.AddReference("PresentationFramework")
clr.AddReference("System.Xml")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
import System.Windows.Controls
from System.Windows.Controls import *
from System.IO import StringReader
from System.Xml import XmlReader
from System.Windows import LogicalTreeHelper
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import Window, Application
import traceback
class MainWindow(Window):
string_xaml = '''
<window height="700" minheight="700" minwidth="780" title="Selection" width="700" x:name="MainWindow" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<window .resources="">
<!--perform Single click checkbox selection in WPF DataGrid like DataGridView.Editmode = EditOnEnter on WinForm-->
<style targettype="DataGridCell">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="DataGridCell.IsReadOnly" Value="False" />
<Condition Property="DataGridCell.IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="IsEditing" Value="True" />
<Setter Property="Background" Value="LightGreen" />
</MultiTrigger>
</Style.Triggers>
</style>
</window>
<grid height="auto" width="auto">
<grid .rowdefinitions="">
<rowdefinition height="30">
<rowdefinition>
<rowdefinition height="60">
</rowdefinition></rowdefinition></rowdefinition></grid>
<label content="Selection" grid.column="0" grid.row="0" height="25" horizontalalignment="Left" margin="8,0,366.6,5" verticalalignment="Bottom" width="415" x:name="label1">
<datagrid canuseraddrows="False" grid.column="0" grid.row="1" horizontalalignment="Stretch" margin="8,3,8,7" selectionunit="Cell" verticalalignment="Stretch" x:name="dataGrid">
</datagrid>
<button content="Annuler" grid.column="0" grid.row="2" height="30" horizontalalignment="Left" margin="18,13,0,10" verticalalignment="Bottom" width="120" x:name="buttonCancel">
</button>
<button content="OK" grid.column="0" grid.row="2" height="30" horizontalalignment="Right" margin="0,12,22,10" verticalalignment="Bottom" width="120" x:name="buttonOK">
</button>
</label></grid>
</window>
'''
def __init__(self, tableData, outColumnName, hide_out_Column):
super().__init__()
self._tableData = tableData
self._outColumnName = outColumnName
self._hide_out_Column = hide_out_Column
#
xr = XmlReader.Create(StringReader(MainWindow.string_xaml))
self.winLoad = XamlReader.Load(xr)
self.outSelection = []
self.InitializeComponent()
def InitializeComponent(self):
try:
self.Content = self.winLoad.Content
#
self.dataGrid = LogicalTreeHelper.FindLogicalNode(self.winLoad, "dataGrid")
self.dataGrid.SelectedCellsChanged += self.DataGrid_CurrentCellChanged
#
self.buttonCancel = LogicalTreeHelper.FindLogicalNode(self.winLoad, "buttonCancel")
self.buttonCancel.Click += self.ButtonCancelClick
#
self.buttonOK = LogicalTreeHelper.FindLogicalNode(self.winLoad, "buttonOK")
self.buttonOK.Click += self.ButtonOKClick
#
self.winLoad.Loaded += self.OnLoad
#
self.dataGrid.ItemsSource = self._tableData.DefaultView
except Exception as ex:
print(traceback.format_exc())
def DataGrid_CurrentCellChanged(self, sender, e):
currentDataGrid = sender
try:
lst_Selected = [cell.Item["Selection"] for cell in currentDataGrid.SelectedCells if isinstance(cell.Item["Selection"], (bool, System.Boolean))]
print("currentDataGrid.SelectedCells", currentDataGrid.SelectedCells.Count, lst_Selected)
for idx, cell in enumerate(currentDataGrid.SelectedCells):
dataGridRowView =cell.Item
if currentDataGrid.CurrentCell.Column.DisplayIndex == 0 :
# use the last select value and get the reverse
dataGridRowView["Selection"] = not lst_Selected[-1]
# Refresh DataGrid
currentDataGrid.Items.Refresh()
except Exception as ex:
print(traceback.format_exc())
def OnLoad(self, sender, e):
print("UI loaded")
try:
if self._hide_out_Column:
out_column = next((c for c in self.dataGrid.Columns if str(c.Header) == self._outColumnName ), None)
if out_column is not None:
self.dataGrid.Columns.get_Item(out_column.DisplayIndex).MaxWidth = 0
self.dataGrid.Items.Refresh()
except Exception as ex:
print(traceback.format_exc())
def ButtonCancelClick(self, sender, e):
self.outSelection = []
self.winLoad.Close()
def ButtonOKClick(self, sender, e):
try:
self.outSelection = [row[self._outColumnName] for row in self.dataGrid.Items if row["Selection"] == True]
self.winLoad.Close()
except Exception as ex:
print(traceback.format_exc())
def get_DataTableFromList(header_array, data):
dt = DataTable("CustomData")
# Create columns
dt.Columns.Add("Selection", System.Boolean) # add Column selection
for item, value in zip(header_array, data[0]):
try:
type_value = value.GetType()
except:
type_value = type(value)
dt.Columns.Add(item, type_value)
# Add rows
for sublst_values in data:
sublst_values.insert(0, False) # for Column selection
dt.Rows.Add(*sublst_values)
return dt
header_array = IN[0]
lst_data = IN[1]
out_ColumnName = IN[2]
hide_out_Column = IN[3]
dt = get_DataTableFromList(header_array, lst_data)
objWindow = MainWindow(dt, out_ColumnName, hide_out_Column)
objWindow.winLoad.ShowDialog()
OUT = objWindow.outSelection