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
-
I've done the test, but I don't read and write the text and emoji.
-
@shinya.ta for me letters are read aloud at each move but only some emoji are read.
Please verify that you have copied my code with correct indentations. -
I've done the test. I don't read aloud. I don't read the text either.
Is the place to insert the cord bad?
-
@shinya.ta did you replace the two def say_char and select_range like in my post?
-
-
@shinya.ta I'm not sure you have the same code as me.
Please, if you have an error, print the trace back and copy/paste it in the forum here -
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)
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 it is what I believed: you did not add the line say_char(tv) at end of this def
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
-
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.
-