Cannot import FontWeight for Eto Form

#1

I want to make the text of the RadioButtonList controls in my Eto form bold but there is no Font option for this control like there is for a Label control. So it seemed impossible. But by using the debugger, I found a way to make this happen by changing the FontWeight of the ControlObject for the form. However I could find no way to import FontWeight from Eto.Forms or Eto.Drawing. To get around this, I borrowed the FontWeight of a Label control in my form which has a bold font:

self.ControlObject.FontWeight = self.OperationLabel.ControlObject.FontWeight

The OperationLabel control in my form has its Font option specified with bold using:

Font = Font('Microsoft Sans Serif', HeaderFont, FontStyle.Bold)

and Font can be imported from Eto.Drawing.

This borrowing to specify the FontWeight is a poor workaround and I would much rather use:

self.ControlObject.FontWeight = FontWeight.Bold

but I need help in getting FontWeight imported into my Python script.

It may seem obscure to use ControlObject in order to change the weight of the default font used in the text of all the controls but I found this by using the debugger to trace to where the default text font of all controls is defined. And it works! In my ETO form below, the text of all the controls, including the RadionButtonList controls, is now bold:

What I have not been able to discover is how to make the title of the form Bold. This is now the weakest part of the form. It would be great if this could be improved.

Regards,
Terry.

(Dale Fugier) #2

How about this?

import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms

class SimpleEtoDialog(forms.Dialog):

    def __init__(self):
        self.Title = "Sample Eto Dialog"
        self.ClientSize = drawing.Size(200, 200)
        self.Padding = drawing.Padding(5)
        self.Resizable = False

        self.Label = forms.Label()
        self.Label.Text = "Hello Rhino.Python!"
        self.Content = self.Label
        
        self.Load += self.OnLoadDialog
    
    def OnLoadDialog(self, sender, e):
        font = self.Label.Font
        self.Label.Font = drawing.Font(font.FamilyName, font.Size, drawing.FontStyle.Bold)
        
        
def TestSampleEtoDialog():
    dialog = SimpleEtoDialog()
    dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)

if __name__ == "__main__":
    TestSampleEtoDialog()    

– Dale

#3

Dale,

Thank you for your response. In the example you gave, the Label control already allows a font specification as shown below in my revised version of your example. There is no need to add a callback and its handler.

import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms
from Eto.Drawing import Size, Font, FontStyle

class SimpleEtoDialog(forms.Dialog):

	def __init__(self):
		self.Title = "Sample Eto Dialog"
		self.Padding = drawing.Padding(5)
		self.Resizable = False

		layout = forms.DynamicLayout(Padding = drawing.Padding(5), Spacing = drawing.Size(5,5))
		self.HelloLabel = forms.Label(
			Text = "Hello Rhino.Python!",
			Font = Font('Microsoft Sans Serif', 10., FontStyle.Bold))
		self.EnterSelect = forms.RadioButtonList(
			DataStore = ['Enter', 'Select', 'Reuse'],
			SelectedIndex = 0,
			Spacing = Size(9,0))
		layout.AddRow(self.HelloLabel)
		layout.AddRow(self.EnterSelect)

		self.Content = layout

def TestSampleEtoDialog():
	dialog = SimpleEtoDialog()
	dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)

if __name__ == "__main__":
	TestSampleEtoDialog()

The bigger challenge is to change the text on controls that do not have a Font option like the RadioButtonList. This is what lead me to adding this one line after the self.Content = layout line:
self.ControlObject.FontWeight = self.HelloLabel.ControlObject.FontWeight
which is in the second revised definition of your form below:

import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms
from Eto.Drawing import Size, Font, FontStyle

class SimpleEtoDialog(forms.Dialog):

	def __init__(self):
		self.Title = "Sample Eto Dialog"
		self.Padding = drawing.Padding(5)
		self.Resizable = False

		layout = forms.DynamicLayout(Padding = drawing.Padding(5), Spacing = drawing.Size(5,5))
		self.HelloLabel = forms.Label(
			Text = "Hello Rhino.Python!",
			Font = Font('Microsoft Sans Serif', 10., FontStyle.Bold))
		self.EnterSelect = forms.RadioButtonList(
			DataStore = ['Enter', 'Select', 'Reuse'],
			SelectedIndex = 0,
			Spacing = Size(9,0))
		layout.AddRow(self.HelloLabel)
		layout.AddRow(self.EnterSelect)

		self.Content = layout
		self.ControlObject.FontWeight = self.HelloLabel.ControlObject.FontWeight

def TestSampleEtoDialog():
	dialog = SimpleEtoDialog()
	dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)

if __name__ == "__main__":
	TestSampleEtoDialog()

The inclusion of this one line will change the font used in the text of all controls but it does not prevent customizing the font of any control that permits a font specification. The local font definition in the control will override this line.

But the proper way to define the FontWeight is not by stealing it from another control that allows a font specification but by importing it from some module. But I cannot get this import to work with Eto.Forms or Eto.Drawing. So where is it hidden? Obviously it exists because the HelloLabel uses it. But from where?

Then on top of this we have the more interesting puzzle of how to punch up the wimpy text in the title of the form with a bigger font, bold style and color.

Regards,
Terry.

(Dale Fugier) #4

@curtisw can you help here?

(Curtis Wensley) #6

Hey @Terry_Chappell,

Unfortunately, using ControlObject.FontWeight is windows (WPF) specific, so you need to do this in a different way for it to work cross platform (if that is a requirement). Eto doesn’t directly expose a way to change the font of the RadioButtonList as you noted, but you can change it via the new cascading styles like so (C#):

myRadioButtonList.Styles.Add<RadioButton>(null, b => b.Font = Eto.Drawing.SystemFonts.Bold());

There really should be a way to set the font in an easier way, e.g. myRadioButtonList.Font = SystemFonts.Bold(), I’ve added an issue here to take a look at that.

Hope this helps!

#8

Thanks for your response.

How do I code this in Python?

Regards,
Terry.

(Curtis Wensley) #9

Hey Terry, my Python skills are very rusty but let me try it out…

I also just realized the cascading styles feature in Eto is not in SR14, so here’s an alternate way to do it using the LoadComplete event:

class SimpleEtoDialog(forms.Dialog):

    def __init__(self):
        # snip...
        self.radioButtons = forms.RadioButtonList()
        self.radioButtons.LoadComplete += self.radioButtonsLoaded
        self.radioButtons.Items.Add("First")
        self.radioButtons.Items.Add("Second")

   def radioButtonsLoaded(self, sender, e):
        for l in self.radioButtons.Children:
          if type(l) is forms.RadioButton:
            l.Font = drawing.SystemFonts.Bold()

in SR15, you should be able to do this instead, which would work for all RadioButtonList controls in your form:

class SimpleEtoDialog(forms.Dialog):

    def __init__(self):
        # sets style of RadioButton on ALL RadioButtonLists in this window
        self.Styles.Add[forms.RadioButton](None, self.setFont)
        # snip...

    def setFont(self, radioButton): radioButton.Font = drawing.SystemFonts.Bold()

Cheers!
Curtis.

#10

Curtis,

Thanks for your help on this. I will try it out soon.

How soon before the SR15 update shows up on the Mac side in Rhino 6 WIP?

Regards,
Terry.

#11

Can anything be done to make the form title bold? This is the last remaining piece.

(Curtis Wensley) #12

Unfortunately, there doesn’t appear to be an easy way to do that, other than just changing your system preferences. From my searches so far (for WPF), the only way to do it is to turn off the border by setting myForm.WindowStyle = WindowStyle.None and implement your own title bar.

Curtis.

#13

Curtis,

Thanks for checking on this. IMO, the architecture of the Eto forms should be improved to include controlling how the title appears. I can dress up the rest of the form but the title remains wimpy:

Regards,
Terry.

(Curtis Wensley) #14

I agree that it would be nice to have more control over the title bar. Unfortunately, Eto.Forms is limited to what the platform can actually do (in this case, WPF). If you can’t you can’t change how the title bar appears in WPF, then there’s no way that Eto could do it either.

#15

Curtis,

I took your code suggestion for bolding the RadioButton and made it more general so it works with any layout I define. After each layout for a section of the form is completed, I include a callback to BoldRadioButtons and use the sender argument to test for the presence of a RadioButton. Below is a working example for many of the RadioButtons in my form: Trim Mesh To Curve.

import Eto.Drawing as drawing
from Eto.Drawing import Font, FontStyle, Colors as EtoColors, Size
import Eto.Forms as forms
from Eto.Forms import TextAlignment
from Rhino import UI,RhinoDoc

class TrimMeshDialog(forms.Dialog[bool]):
	# Initializer which creates form.
	def __init__(self):
		# Initialize dialog box.
		self.Padding = drawing.Padding(5)
		self.Title = 'Trim Mesh to Curve'
		# Create main layout.
		layout = forms.DynamicLayout(Padding = drawing.Padding(4,0,4,4), Spacing = drawing.Size(5,5))
		layout.AddSeparateRow(self.CreateRadioButtonsForOperations())
		layout.AddSeparateRow(self.CreateRadioButtonsForBoundaryCurve())
		# Set the dialog content.
		self.Content = layout

	# Make RadioButton text bold.
	def BoldRadioButtons(self, sender, e):
		for l in sender.Children:
			if type(l) is forms.RadioButton:
				l.Font = drawing.SystemFonts.Bold()

	# Controls for selecting operation.
	def CreateRadioButtonsForOperations(self):
		# Create label for radio buttons.
		self.OperationLabel = forms.Label(
			Text = 'Operation',
			Font = Font('Segoe UI', 10, FontStyle.Bold),
			TextColor = EtoColors.Blue)
		# Create radio buttons.
		self.TrimHolePit = forms.RadioButtonList(
			DataStore = ['Trim', 'Hole ', 'Pit', 'Split'],
			SelectedIndex = 0,
			Padding = drawing.Padding(0,0,0,0),
			Spacing = Size(1,0))
		# Create Dynamic layout.
		self.OperationsPanel = forms.DynamicLayout(Spacing = drawing.Size(0,0))
		self.OperationsPanel.AddRow(self.OperationLabel)
		self.OperationsPanel.AddRow(self.TrimHolePit)
		self.OperationsPanel.LoadComplete += self.BoldRadioButtons
		return self.OperationsPanel

	# Controls that define the source for the boundary curve and enable calcuating the mesh volume for Trim operations.
	def CreateRadioButtonsForBoundaryCurve(self):
		# Create label for radio buttons.
		self.BdryLabel = forms.Label(
			Text = 'Boundary Curve',
			Font = Font('Segoe UI', 10, FontStyle.Bold),
			TextColor = EtoColors.Blue)
		self.EnterSelect = forms.RadioButtonList(
			DataStore = ['Enter', 'Select', 'Reuse'],
			SelectedIndex = 0,
			Padding = drawing.Padding(0,0,0,0),
			Spacing = Size(9,0))
		self.LineLabel = forms.Label(
			Text = 'Type of line between points',
			TextAlignment = TextAlignment.Center,
			Font = Font('Segoe UI', 10., FontStyle.Bold),
			TextColor = EtoColors.Green,
			Visible = True)#self.Args.EnterSelect != 2)
		self.StraightCurved = forms.RadioButtonList(
			DataStore = ['Straight ', 'Curved'],
			SelectedIndex = 0,
			Visible = True,
			Padding = drawing.Padding(20,0,0,0),
			Spacing = Size(10,0))
		# Create Dynamic layout.
		self.BoundaryCurvePanel = forms.DynamicLayout(Padding = drawing.Padding(0,0), DefaultSpacing = drawing.Size(5,0))
		self.BoundaryCurvePanel.AddRow(self.BdryLabel)
		self.BoundaryCurvePanel.AddRow(self.EnterSelect)
		self.BoundaryCurvePanel.AddRow(self.StraightCurved)
		self.BoundaryCurvePanel.LoadComplete += self.BoldRadioButtons
		return self.BoundaryCurvePanel

dlg = TrimMeshDialog()
result = UI.EtoExtensions.ShowSemiModal(dlg, RhinoDoc.ActiveDoc, UI.RhinoEtoApp.MainWindow)

Thanks for your great help with this.

Regards,
Terry.