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.
pythonista UI
-
There is a AttributeError on the twenty lines.
import ui
from objc_util import *
import clipboard
import speechv = ui.View()
v.frame = (0,0,500,320)
v.name = 'Move cursor in TextView'tv = ui.TextView()
tv.name = 'TextView'
tv.frame = (120,10,370,300)
tv.font = ('Arial Rounded MT Bold',24)
tv.text = 'aรฉ๐ข๐ฏ๐ต๐จโ๐จโ๐งโ๐ง'
v.add_subview(tv)def say_char(tv):
# test speech character at cursor
idxtopos = IndexToPos('') # list index to position
i = tv.selected_range[0]
#print(i,idxtopos)
i = idxtopos[i] # used to check if same base character
if i < len(tv.text):
c = tv.text[i]
if c == ' ':
c ='space'
speech.say(c,'jp-JP')def selected_range(i):
tvo = ObjCInstance(tv)
p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
p2 = p1
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1, p2)
say_char(tv)
returnsome emoji like flags count as 2 for len but as 4 for selected_range
def IndexToPos(type):
tvo = ObjCInstance(tv)
# build array index -> position in range
idxtopos = []
pre_x = -1
#print(tv.text)
for c in tv.text:
# nbr characters used e=1 รฉ=1 ๐=1 ๐ฏ๐ต=2 ๐จโ๐จโ๐งโ๐ง=7
# some emoji generate more than one character,
# sometimes counted for more than one in range
# 1,2,3->1 4->2
nb = 1 + int(len(c.encode('utf-8'))/4)
for j in range(0,nb):
p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), len(idxtopos))
p2 = p1
rge = tvo.textRangeFromPosition_toPosition_(p1, p2)
rect = tvo.firstRectForRange_(rge) # CGRect
x = rect.origin.x
if x == float('inf') or x == pre_x:
# same x as previous one, composed character
pass
else:
pre_x = x
i = len(idxtopos)
idxtopos.append(i) # start position of c
#print(c,nb,len(idxtopos)-1,i,x)
idxtopos.append(i+1) # end position of last c
#print(idxtopos)
# get index of actual cursor
i = tv.selected_range[0] # actual position of cursor
# often p is one of sub_chars, not always the first one
p = idxtopos[i] # used to check if same base character
#print(p,i,idxtopos)
if type == 'left':
if i == 0:
return # already before character
while True:
i = i - 1
if idxtopos[i] != p:
q = idxtopos[i]
# seach first sub-character
while i > 0:
if idxtopos[i-1] != q:
break
i = i - 1
break
elif type == 'right':
if i == (len(idxtopos)-1):
return # already after last character
while True:
i = i + 1
if idxtopos[i] != p:
break
elif type == 'end':
i = len(idxtopos)-1
else:
return idxtopos
r = idxtopos[i]
say_char(i)
return idxtopos -
@shinya.ta Please, if you have an error, print the trace back and copy/paste it in the forum here. This is the clear description of the error. I'm sorry for you but I don' t have this error with my code. And when you put your code in the forum, you never use the </> button to insert a code and KEEPING its indentation
-
@shinya.ta I've found your error: 3 lines before b_top....
Put
selected_range(i) return idxtopos b_top = ui.Button()
Instead of
say_char(i) return idxtopos b_top = ui.Button()
-
import ui
from objc_util import *
import clipboard
import speechv = ui.View()
v.frame = (0,0,500,320)
v.name = 'Move cursor in TextView'tv = ui.TextView()
tv.name = 'TextView'
tv.frame = (120,10,370,300)
tv.font = ('Arial Rounded MT Bold',24)
tv.text = 'aรฉ๐ข๐ฏ๐ต๐จโ๐จโ๐งโ๐ง'
v.add_subview(tv)def say_char(tv):
# test speech character at cursor
idxtopos = IndexToPos('') # list index to position#print(c,nb,len(idxtopos)-1,i,x) idxtopos.append(i+1) # end position of last c #print(idxtopos) # get index of actual cursor i = tv.selected_range[0] # actual position of cursor # often p is one of sub_chars, not always the first one p = idxtopos[i] # used to check if same base character #print(p,i,idxtopos) if type == 'left': if i == 0: return # already before character while True: i = i - 1 if idxtopos[i] != p: q = idxtopos[i] # seach first sub-character while i > 0: if idxtopos[i-1] != q: break i = i - 1 break elif type == 'right': if i == (len(idxtopos)-1): return # already after last character while True: i = i + 1 if idxtopos[i] != p: break elif type == 'end': i = len(idxtopos)-1 else: return idxtopos r = idxtopos[i] say_char(i) return idxtopos
i = tv.selected_range[0]
#print(i,idxtopos)
i = idxtopos[i] # used to check if same base character
if i < len(tv.text):
c = tv.text[i]
if c == ' ':
c ='space'
speech.say(c,'jp-JP')```
def selected_range(i):
tvo = ObjCInstance(tv)
p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
p2 = p1
tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1, p2)
returnsome emoji like flags count as 2 for len but as 4 for selected_range
def IndexToPos(type):
tvo = ObjCInstance(tv)
# build array index -> position in range
idxtopos = []
pre_x = -1
#print(tv.text)
for c in tv.text:
# nbr characters used e=1 รฉ=1 ๐=1 ๐ฏ๐ต=2 ๐จโ๐จโ๐งโ๐ง=7
# some emoji generate more than one character,
# sometimes counted for more than one in range
# 1,2,3->1 4->2
nb = 1 + int(len(c.encode('utf-8'))/4)
for j in range(0,nb):
p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), len(idxtopos))
p2 = p1
rge = tvo.textRangeFromPosition_toPosition_(p1, p2)
rect = tvo.firstRectForRange_(rge) # CGRect
x = rect.origin.x
if x == float('inf') or x == pre_x:
# same x as previous one, composed character
pass
else:
pre_x = x
i = len(idxtopos)
idxtopos.append(i) # start position of c```
Insert Code Here -
I'm sorry.
I'm not sure how to send this code.
The change of the code has already finished, but there is no change.I'm confused.
-
@shinya.ta Sorry but not readable, que you sure you use this
-
@shinya.ta hรจre is my entire code
import ui from objc_util import * import clipboard import speech v = ui.View() v.frame = (0,0,500,320) v.name = 'Move cursor in TextView' tv = ui.TextView() tv.name = 'TextView' tv.frame = (120,10,370,300) tv.font = ('Arial Rounded MT Bold',24) tv.text = 'aรฉ๐ข๐ฏ๐ต๐จโ๐จโ๐งโ๐ง' v.add_subview(tv) def say_char(tv): # test speech character at cursor idxtopos = IndexToPos('') # list index to position i = tv.selected_range[0] #print(i,idxtopos) i = idxtopos[i] # used to check if same base character if i < len(tv.text): c = tv.text[i] if c == ' ': c ='space' speech.say(c,'jp-JP') def selected_range(i): tvo = ObjCInstance(tv) p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i) p2 = p1 tvo.selectedTextRange = tvo.textRangeFromPosition_toPosition_(p1, p2) say_char(tv) return # some emoji like flags count as 2 for len but as 4 for selected_range def IndexToPos(type): tvo = ObjCInstance(tv) # build array index -> position in range idxtopos = [] pre_x = -1 #print(tv.text) for c in tv.text: # nbr characters used e=1 รฉ=1 ๐=1 ๐ฏ๐ต=2 ๐จโ๐จโ๐งโ๐ง=7 # some emoji generate more than one character, # sometimes counted for more than one in range # 1,2,3->1 4->2 nb = 1 + int(len(c.encode('utf-8'))/4) for j in range(0,nb): p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), len(idxtopos)) p2 = p1 rge = tvo.textRangeFromPosition_toPosition_(p1, p2) rect = tvo.firstRectForRange_(rge) # CGRect x = rect.origin.x if x == float('inf') or x == pre_x: # same x as previous one, composed character pass else: pre_x = x i = len(idxtopos) idxtopos.append(i) # start position of c #print(c,nb,len(idxtopos)-1,i,x) idxtopos.append(i+1) # end position of last c #print(idxtopos) # get index of actual cursor i = tv.selected_range[0] # actual position of cursor # often p is one of sub_chars, not always the first one p = idxtopos[i] # used to check if same base character #print(p,i,idxtopos) if type == 'left': if i == 0: return # already before character while True: i = i - 1 if idxtopos[i] != p: q = idxtopos[i] # seach first sub-character while i > 0: if idxtopos[i-1] != q: break i = i - 1 break elif type == 'right': if i == (len(idxtopos)-1): return # already after last character while True: i = i + 1 if idxtopos[i] != p: break elif type == 'end': i = len(idxtopos)-1 else: return idxtopos r = idxtopos[i] selected_range(i) return idxtopos b_top = ui.Button() b_top.frame = (10,10,100,32) b_top.title = 'begin' b_top.background_color = 'white' b_top.border_width = 1 def b_top_action(sender): tv = sender.superview['TextView'] tv.selected_range = (0,0) say_char(tv) b_top.action = b_top_action v.add_subview(b_top) b_left = ui.Button() b_left.frame = (10,50,100,32) b_left.title = 'left' b_left.background_color = 'white' b_left.border_width = 1 def b_left_action(sender): idxtopos = IndexToPos('left') # list index to position b_left.action = b_left_action v.add_subview(b_left) b_right = ui.Button() b_right.frame = (10,90,100,32) b_right.title = 'right' b_right.background_color = 'white' b_right.border_width = 1 def b_right_action(sender): idxtopos = IndexToPos('right') # list index to position b_right.action = b_right_action v.add_subview(b_right) b_bottom = ui.Button() b_bottom.frame = (10,130,100,32) b_bottom.title = 'end' b_bottom.background_color = 'white' b_bottom.border_width = 1 def b_bottom_action(sender): idxtopos = IndexToPos('end') # list index to position b_bottom.action = b_bottom_action v.add_subview(b_bottom) def get_xy(tv): idxtopos = IndexToPos('') # list index to position tvo = ObjCInstance(tv) x_y = [] for i in range(0,len(idxtopos)+1): # x,y of each character p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i) rge = tvo.textRangeFromPosition_toPosition_(p1,p1) rect = tvo.firstRectForRange_(rge) # CGRect x,y = rect.origin.x,rect.origin.y if i == len(idxtopos): if i > 0: x,y = x_y[i-1] else: # text is empty x,y = 0,0 if x == float('inf'): x,y = x_prec+15,y_prec x_prec,y_prec = x,y x_y.append((x,y)) return x_y b_up = ui.Button() b_up.frame = (10,170,100,32) b_up.title = 'up' b_up.background_color = 'white' b_up.border_width = 1 def b_up_action(sender): tv = sender.superview['TextView'] x_y = get_xy(tv) c = tv.selected_range[0] xc,yc = x_y[c] i = c - 1 while i >= 0: x,y = x_y[i] if y < yc: # previous row if x <= xc: selected_range(i) return i = i - 1 b_up.action = b_up_action v.add_subview(b_up) b_down = ui.Button() b_down.frame = (10,210,100,32) b_down.title = 'down' b_down.background_color = 'white' b_down.border_width = 1 def b_down_action(sender): tv = sender.superview['TextView'] idxtopos = IndexToPos('') # list index to position x_y = get_xy(tv) c = tv.selected_range[0] #print(x_y,c) xc,yc = x_y[c] i = c# - 1 # I don't remember why this "- 1" while i < len(idxtopos): x,y = x_y[i] if y > yc: # next row if x >= xc: selected_range(i) return else: if (i+1) < len(idxtopos): if x_y[i+1][1] > y: # i = last character of row under cursor selected_range(i) return else: pass # try next x else: # last character of last row selected_range(i) return i = i + 1 b_down.action = b_down_action v.add_subview(b_down) b_copy = ui.Button() b_copy.frame = (10,250,100,32) b_copy.title = 'copy' b_copy.background_color = 'white' b_copy.border_width = 1 def b_copy_action(sender): tv = sender.superview['TextView'] clipboard.set(tv.text) b_copy.action = b_copy_action v.add_subview(b_copy) b_clear = ui.Button() b_clear.frame = (10,290,100,32) b_clear.title = 'clear' b_clear.background_color = 'white' b_clear.border_width = 1 def b_clear_action(sender): tv = sender.superview['TextView'] tv.text = '' b_clear.action = b_clear_action v.add_subview(b_clear) v.present('sheet') tv.selected_range = (0,0) tv.begin_editing()```
-
It's done.
I misunderstood the code I changed.
Thank you very much.
Thank you very much.I had tears.
You are really my God.
-
@shinya.ta ๐ญ --> ๐
-
@shinya.ta I suppose your setting Settings -> General -> Accessibility -> Speech -> Speak Selection and check if the ON/OFF switch is green (active) is ON
In this case, if you try to select all your text, you'll see a speak option, try it and all letters and emoji are read aloud. It is perhaps possible to use objectivec code instead of the Pythonista speech to read so.
Try Spell also, marvelous
-
I did not know that there is such a wonderful function.
I am working now, so I will try it when I go home.
Thank you for your wonderful advice.
After all, you are God.
-
-
This time, I added the following code.
I selected the emoji I read aloud and put it in the task bar.
When the Text View is blank, when you press the cursor move button, there is an error.
What's the cause?
#็ตตๆๅญใฟในใฏใใผ
def typeChar(sender):
'''finds active textinput, and types the button's title'''
tf=sender.objc_instance.firstResponder()
tf.insertText_(sender.title)#create normal keys
buttons=[]
for button_title in '๐๐๐ฑ๐ฆโ๏ธ':
buttons.append(ui.Button(title=button_title,action=typeChar))#create special keys
def prev(sender):
'''simulates 'tab' key, go to next field '''
s=sender.objc_instance.firstResponder()._previousKeyResponder().becomeFirstResponder()
buttons.append(ui.Button(image=ui.Image.named('iob:ios7_arrow_back_32'),action=prev))def next(sender):
'''simulates 'tab' key, go to next field '''
s=sender.objc_instance.firstResponder()._nextKeyResponder().becomeFirstResponder()
buttons.append(ui.Button(image=ui.Image.named('iob:ios7_arrow_forward_32'),action=next))'''set up toolbar'''
keyboardToolbar=ObjCClass('UIToolbar').alloc().init()
keyboardToolbar.sizeToFit()
keyboardToolbar.items = [ObjCClass('UIBarButtonItem').alloc().initWithCustomView_(b) for b in buttons]#attach our accessory to the textfield, and textview.
tv.objc_instance.setInputAccessoryView_(keyboardToolbar)v.present('sheet')
tv.selected_range = (0,0)
tv.begin_editing() -
@shinya.ta Is she able to select the text and to tap the spell button?
-
I think training is necessary, but I think it's possible.
-
@shinya.ta add "i = 0" before "for c..." in "def IndexToPos..." as here-under
def IndexToPos(type): tvo = ObjCInstance(tv) # build array index -> position in range idxtopos = [] pre_x = -1 #print(tv.text) i = 0 for c in tv.text:```
-
Thank you very much.
We were able to solve it immediately.
There is a "โ๏ธโถ๏ธ" in the task bar, so I don't think that's necessary.
I'm trying to remove it, but I'm not sure which one I should turn off. -
I want to increase the type of emoji in the task bar, but when I try to increase the number of emoji, the original emoji will hide and I can't display them.
-
@shinya.ta Try this
#create special keys def prev(sender): '''simulates 'tab' key, go to next field ''' s=sender.objc_instance.firstResponder()._previousKeyResponder().becomeFirstResponder() buttons.append(ui.Button(image=ui.Image.named('iob:ios7_arrow_back_32'),action=prev)) def next(sender): '''simulates 'tab' key, go to next field ''' s=sender.objc_instance.firstResponder()._nextKeyResponder().becomeFirstResponder() buttons.append(ui.Button(image=ui.Image.named('iob:ios7_arrow_forward_32'),action=next)) #create normal keys d = 32 dd = 4 w, h = ui.get_screen_size() vv = ui.View() vv.background_color = 'lightgray' h = 0 x = dd y = dd for button_title in '๐๐๐ฑ๐ฆโ๏ธ๐๐๐๐๐๐ ๐๐คฃโบ๏ธ๐๐๐๐๐๐๐๐ฅฐ๐๐๐๐๐๐๐๐๐คช๐คจ๐ง๐ค๐๐คฉ๐ฅณ๐๐๐๐๐๐๐โน๏ธ๐ฃ๐๐ซ๐ฉ๐ฅบ๐ข๐ญ๐ค๐ ๐ก๐คฌ๐คฏ๐ณ๐ฅต๐ฅถ๐ฑ๐จ๐ฐ๐ฅ๐๐ค๐ค๐คญ๐คซ๐คฅ๐ถ๐๐๐ฌ๐ฆ๐ง๐ฎ๐ฒ๐ด๐คค๐ช๐ต๐ค๐ฅด๐คข๐คฎ๐คง๐ท๐ค๐ค๐ค๐ค ๐': b = ui.Button(title=button_title,action=typeChar) b.frame = (x,y,d,d) b.font = ('.SFUIText', d) if (y+d+dd) > h: h = y + d + dd vv.add_subview(b) x = x + d + dd if (x+d+dd) > w: x = dd y = y + d + dd vv.frame = (0,0,w,h) #attach our accessory to the textfield, and textview. tvo = tv.objc_instance tvo.setInputAccessoryView_(ObjCInstance(vv)) v.present('sheet') tv.selected_range = (0,0) tv.begin_editing()
-