Hello,
I’m working on a user tagging capability that has an Eto Forms Dialog where the user can add additional “tags” to a Rhino object.
Currently I can only get this working in a StackLayout configuration but I’m trying to get it to first fill up with rows and when the tags exceed or get close to exceeding the width of the dialogue window, a new row should be created for the additional tags, for as many times as this step is necessary.
Here’s my dialog currently:
And I’m trying to get it to layout like this:
I believe I need to use DynamicLayout for this but I can’t seem to get it working regardless of the methods I use. I think I’m fundamentally not understanding how to “nest” multiple items per row or something along those lines.
Any help is greatly appreciated!
Here is my full working Python code thus far:
import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms
import rhinoscriptsyntax as rs
class RoundedPanel(forms.Panel):
def __init__(self):
super(RoundedPanel, self).__init__()
self.radius = 5
def OnPaint(self, e):
rect = drawing.RectangleF(0, 0, self.Width, self.Height)
brush = drawing.SolidBrush(self.BackgroundColor)
e.Graphics.FillRoundedRectangle(brush, rect, self.radius)
brush.Dispose()
class SimpleEtoDialog(forms.Dialog):
def __init__(self):
Rhino.UI.EtoExtensions.UseRhinoStyle(self)
self.Title = "Tag Object"
self.ClientSize = drawing.Size(300, 200)
self.Padding = drawing.Padding(5)
self.Resizable = False
# Initialize tag_text as an empty string
self.tag_text = ""
# Label
self.label = forms.Label()
self.labelBox = forms.TextBox(PlaceholderText="Enter tag here") # Provide the placeholder text
self.labelBox.Height = 25 # Adjust the height of the text box
self.labelBox.KeyDown += self.OnTextBoxKeyDown
# Content panel for text labels
self.content_panel = forms.StackLayout() # Use StackLayout instead of FlowLayout
self.content_panel.Spacing = 5 # Set spacing between controls
# Layout
layout = forms.DynamicLayout()
layout.AddRow(self.label)
layout.AddRow(self.labelBox)
layout.AddRow(self.content_panel) # Add content_panel to the layout
self.Content = layout
# Populate existing tags
self.populate_existing_tags()
def populate_existing_tags(self):
# Get selected objects
selected_objs = rs.SelectedObjects()
if selected_objs:
# Iterate over selected objects
for obj in selected_objs:
existing_tags = rs.GetUserText(obj, "Tags")
if existing_tags:
# Split existing tags and add them to the dialog
tags_list = existing_tags.split(",")
for tag in tags_list:
self.add_tag_label(tag.strip())
def add_tag_label(self, tag_text):
# Create a new panel for the text label with rounded corners and background color
label_panel = RoundedPanel()
label_panel.Padding = drawing.Padding(5)
label_panel.BackgroundColor = drawing.Color(220,220,220) # Set background color
label_panel.Content = forms.Label(Text=tag_text)
# Add the panel to the content panel
self.content_panel.Items.Add(label_panel)
def OnTextBoxKeyDown(self, sender, e):
if e.Key == forms.Keys.Enter:
# Get the text entered into the text box
new_tag_text = self.labelBox.Text.strip()
if new_tag_text:
# Add the new tag to the dialog
self.add_tag_label(new_tag_text)
# Clear the text box
self.labelBox.Text = ""
# Append tags to selected objects
selected_objs = rs.SelectedObjects()
if selected_objs:
tags = [new_tag_text]
for obj in selected_objs:
append_tags(obj, tags)
else:
print("No objects selected.")
def append_tags(obj, additional_values):
existing_value = rs.GetUserText(obj, "Tags")
if existing_value:
existing_values = existing_value.lower().split(",") # Convert existing values to lowercase and split them
new_values = [val.lower() for val in additional_values if val.lower() not in existing_values]
if new_values:
new_value = ",".join(existing_values + new_values)
rs.SetUserText(obj, "Tags", new_value)
print("Tags appended successfully.")
else:
print("No new tags added. Tags already exist.")
else:
lowercased_values = [val.lower() for val in additional_values]
rs.SetUserText(obj, "Tags", ",".join(lowercased_values))
print("Tags set successfully.")
################################################################################
# Creating a dialog instance and displaying the dialog.
################################################################################
def TestSampleEtoDialog():
dialog = SimpleEtoDialog()
dialog.Icon = drawing.Icon(r"C:\Users\micha\OneDrive - Michael Vollrath\TOYBLOCK\PROJECTS\PROJECT_ENDGAME\ICONS\ICO_Tag_Object.ico")
dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)
################################################################################
# Check to see if this file is being executed as the "main" python
# script instead of being used as a module by some other python script
# This allows us to use the module which ever way we want.
################################################################################
if __name__ == "__main__":
TestSampleEtoDialog()
This is my first attempt at Eto and any Rhino UI work in general, so far the API documentation has been very helpful, I’m just completely stuck on how to implement the layout here.
Thank you!