Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Superscript Text in ui Button [Solved]
-
Hello all,
I am new to the Pythonista movement, just bought the app recently to try and explore some Python in a fun way. I have experience in C, C++, and Matlab, but not Python so I figured it was worth trying out.
I am currently working on writing an advanced version of the calculator.py tutorial, and I am trying to create buttons for things like "x^y" and "log base 10" in the usual mathematical notation but I cannot enter Button text in different sizes for each character nor can I find a way to print using super / sub scripts. Is there a clever way that I can use the custom attributes field for buttons to accomplish this? Or can I import an image into the button which is a depiction of these characters with no background?
I am trying to do this in the ui editor which comes with Pythonista.
Thanks for your help!
-
There are two options that I can think of:
- Use an image of the formula, like you suggested. You'll probably want to use the default "stencil" image mode, in which all visible parts of the image are "cut out" and displayed in the UI tint color (light blue by default, like the button text). That means you need an image with the formula on a transparent background, probably as a PNG. (A white background wouldn't work - because white is a visible color, it would also be treated as "foreground", and you'd just get a blue rectangle.)
- Construct the formula out of multiple labels and add them as subviews of the button. The UI designer doesn't allow this, but in code you can use the
add_subview
method on the button to add subviews to it. The easiest way to do that is probably to create an extra pyui file for each "special" formula. Then you can place the pyui onto the button usingbutton.add_subview(ui.load_view("x_pow_y"))
for example. This also allows you to reuse the formula in other parts of your UI. (Unfortunately that is also only possible from code - it's not possible to "include" one pyui file in another using the UI designed.)
@omz Is adding subviews to buttons an intentional feature? Just wondering, because the UI designer doesn't allow it.
-
IIRC, buttons support attributedtext, via
objc_util
.There was some code in the forums about adding UIAttributedText to a ui.label, but I am. ot finding it atm. Same basic idea should work for a button.
-
@dgelessus To avoid the limitations of a button image, I often uses an ImageView as a subview of the button.
-
Here's an example how to use superscript in button created in Pythonista UI designer. Several comments:
- ObjC class used in this case is not descendant of
UIButton
class UIButton
in this case is added as subview- Superscript works only and only if font supports it, for example x^2 works (supported by default iOS font), but x^y doesn't (not supported by default iOS font)
- If you'd like to fake it, you can use
NSFontAttributeName
andNSBaselineOffsetAttributeName
import ui from objc_util import * class MyView(ui.View): def did_load(self): x_pow_y_button = self['x_pow_y_button'] NSMutableAttributedString = ObjCClass('NSMutableAttributedString') attributed_string = NSMutableAttributedString.alloc().initWithString_(ns('x2')) range = NSRange(1, 1) attributed_string.superscriptRange(range) x_pow_y_button_objc = ObjCInstance(x_pow_y_button) UIButton = ObjCClass('UIButton') for subview in x_pow_y_button_objc.subviews(): if subview.isKindOfClass(UIButton): subview.setAttributedTitle_forState_(attributed_string, 0) v = ui.load_view() v.present('sheet')
- ObjC class used in this case is not descendant of
-
And here's an example how to fake it if font doesn't support it.
import ui from objc_util import * class MyView(ui.View): def did_load(self): x_pow_y_button = self['x_pow_y_button'] NSMutableAttributedString = ObjCClass('NSMutableAttributedString') attributed_string = NSMutableAttributedString.alloc().initWithString_(ns('xy')) range = NSRange(1, 1) UIFont = ObjCClass('UIFont') attributes = { ns('NSFont'): UIFont.systemFontOfSize(11), ns('NSBaselineOffset'): ns(5) } attributed_string.setAttributes_range_(attributes, range) x_pow_y_button_objc = ObjCInstance(x_pow_y_button) UIButton = ObjCClass('UIButton') for subview in x_pow_y_button_objc.subviews(): if subview.isKindOfClass(UIButton): subview.setAttributedTitle_forState_(attributed_string, 0) v = ui.load_view() v.present('sheet')
-
Just tested the
superscriptRange
way on the iOS 11B4 and it does work. So, maybe the iOS 10.3.3 is broken in some way. Anyway it’s pretty ugly (way too up), so, you should go with smaller font & custom base line offset for your superscripts. At least you can control how it look like. -
Thanks everyone for the replies, I really appreciate the input. I have been thinking about them but haven't had time to really dig in and understand some of the more advanced stuff, like using the obj-c approach. That seems really elegant, but I haven't quite wrapped my head around how the python wrapper for the obj-c works quite yet.
I will post again as soon as I have a chance to attempt some of these things, I have been struggling in the mean time with trying to get different views to draw based on orientation with only moderate luck.
-
Thanks to zrzka for a working solution!
I functionized his answer here:
def superscript(view, itemName, text, size, offset, startPos, length): #Get the UI object by name from the current UI view target_ui_item = view[itemName] #Create a obj-c Mutable Attributed String object and initialize with text NSMutableAttributedString = ObjCClass('NSMutableAttributedString') attributed_string = NSMutableAttributedString.alloc().initWithString_(ns(text)) range = NSRange(startPos, length) #Create an obj-c Font UIFont = ObjCClass('UIFont') #Define a List (?) of attributes attributes = { ns('NSFont'): UIFont.systemFontOfSize(size), ns('NSBaselineOffset'): ns(offset) } #Set the specific attributes of a specific range of the NS Mutable Attributed String attributed_string.setAttributes_range_(attributes, range) # ui_item_objc = ObjCInstance(target_ui_item) UIButton = ObjCClass('UIButton') for subview in ui_item_objc.subviews(): if subview.isKindOfClass(UIButton): subview.setAttributedTitle_forState_(attributed_string, 0)
Into this function, you should pass your current UI.View containing the UI element (in my case a button) which you want the superscripted string.
The second argument is the name of the UI element, in my case 'x_squared' <- the name of my button
The third argument is the text string that you want printed, in my case 'x2'
The fourth argument is the font size of the base character (I used 16)
The fifth arg is the offset for how high (or low, use negative) to offset the smaller character (I used 8)
The sixth arg is the string position to start the superscript string, (for me that was char 1)
The final arg is the length of the superscripted string (also 1 for me)
So the final call to this function looked like this (see first line below)The second line below did a log base 10 string (slightly more complex call than an "x squared" button
superscript(self, 'x_squared', 'x2', 16, 8, 1, 1) superscript(self, 'log_base_ten', 'log10', 16, -10, 3, 2)
Hope this helps anyone who might need this in the future!
Thanks to everyone who helped me!