Aligning text in DataShapes menu

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?

Maybe:

2 Likes

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

1 Like

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.

1 Like

I thought that the DataTable input would work for this, but I guess not.

Some options:

  1. Live with things not aligning
  2. Build your own UI in Python and use that instead of Datashapes
  3. Use two pop-ups, one to show the before, then one to show the after
1 Like

Hi @RevitRobot

I’ve just written an article on selecting objects (or elements) via a DataTable/DataGrid (in WPF), which you may find useful.

Demo Selection by DataGrid wpf

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
3 Likes