I wrote a reader for ebooks, including the index of the book. I use Labels to show text and Buttons to show index, because I need to switch to the new chapter when user click the button.
So I need to change the Label.text and Button.title when scrolling. But it is strange that looks like the performance of Button.title is not good. It shows white pieces when I scroll the bar.
Forum Archive
The performance of button.name is kind of slow?
@lpl Are you sure you don't want to use button.title?
@cvp changed.
@lpl and why do you want to change something in the book index?
You could use a TableView for the index and never change something in it, scrolling is foreseen and select a line is like tapping a button.
https://m.youtube.com/watch?v=vqxlf4iJk20&feature=share
@cvp Maybe the book is very big, then the TableView will be very big too? I can only use 13 Labels to display all the contents now.
@lpl yes, for the labels, but how much indexes?
@cvp 13 Buttons, too. The index may very big, too?
@lpl I have a TableView with 10000 lines, very quick. Don't forget that display lines is done only when needed
@cvp But there is another thing, I also want to display the manhwa imgs. So I have to use ImagView like Label in ebook reader. … And I can't make it with TableView, right? So I use the same way to display the index, ebook and emanhwa.
@lpl Not textview but TableView and you can display what you want, like images, in each line.
The covers of my 13000 ePubs

@cvp I remembered the reason why I didn't use TextView. I can't get the position of the scrollbar. So I use the Labels.
@cvp But I need to display the full images on the screen which may have different sizes.
@lpl ok, but remember, I didn't say textview but TableView which is a scrollview thus you can get the position of the scroll bar
@cvp Sorry. I wrote another word. I understood that, but I'm not a native speaker so sometimes I may wrote the wrong word.
@cvp So, since I use the same strategy in ebook and index. Why there are some white parts when I display the index, and ebook reader looks well?
@cvp The code is open-source. But I have to say, I'm naive and the code is not easy to review, even I kept changing again and again. Maybe you can help me review?
@lpl Not easy to help without the code
@cvp https://github.com/yjqiang/yj_ebook_reader
Run eimg_reader.py or ebook_reader.py, and be careful, there are some 18+ contents.
@cvp I can use telegram to chat, if you often uses it too.
@lpl Sorry but it seems a little bit big to really be able to help.
When you scroll your indexes, the script has to update the images in the buttons, thus it takes some time and if you scroll to quickly, has not enough time to refresh the images?
Your white parts happen when you scroll quickly?
@cvp I use several Labels or Buttons as the subviews of ScrollView. When we scroll, if one subview is hidden(can't be seen by users), then we can move the subview from the top side to the other side.
@cvp And yes, when I scroll quickly, the white part shows. But just for Buttons, not Labels or ImageViews. So I think the performance of Button.title is not good.
@lpl I don't think the title is the reason.
I have import d your code but when I try to run it, I have an error on
Import toml
Toml Not found
@lpl try your buttons without image, just to check
@cvp pip install toml I used toml module in my code. Should write a requirement file.
@cvp one question: when you have to display a new index, do you need to get data from internet? If yes, that could explain why it is slower
@cvp No, I loaded all and then I display them.
@lpl toml loaded, crash
Traceback (most recent call last):
File "/private/var/mobile/Containers/Shared/AppGroup/668A7D98-7216-47ED-917D-AA0B6173167E/Pythonista3/Documents/yj_ebook_reader-master/yj_ebook_reader-master/ebook_reader.py", line 27, in <module>
controller = Controller(EBookLoader, EBookReader)
File "/private/var/mobile/Containers/Shared/AppGroup/668A7D98-7216-47ED-917D-AA0B6173167E/Pythonista3/Documents/yj_ebook_reader-master/yj_ebook_reader-master/controller.py", line 17, in __init__
self.reader_viewer = ReaderViewer(self)
File "/private/var/mobile/Containers/Shared/AppGroup/668A7D98-7216-47ED-917D-AA0B6173167E/Pythonista3/Documents/yj_ebook_reader-master/yj_ebook_reader-master/ereader.py", line 55, in __init__
super().__init__(EBookBodyViewer, controller)
File "/private/var/mobile/Containers/Shared/AppGroup/668A7D98-7216-47ED-917D-AA0B6173167E/Pythonista3/Documents/yj_ebook_reader-master/yj_ebook_reader-master/ereader.py", line 9, in __init__
self.var_index_viewer = IndexViewer(self)
File "/private/var/mobile/Containers/Shared/AppGroup/668A7D98-7216-47ED-917D-AA0B6173167E/Pythonista3/Documents/yj_ebook_reader-master/yj_ebook_reader-master/index_viewer/index_viewer.py", line 12, in __init__
reader_view = ui.load_view('index_viewer/index')
File "/var/containers/Bundle/Application/E4751F4F-64A4-4BE6-AB9D-9C9564715002/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/ui.py", line 412, in load_view
return load_view_str(json_str, bindings, stackframe, verbose=verbose)
File "/var/containers/Bundle/Application/E4751F4F-64A4-4BE6-AB9D-9C9564715002/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/ui.py", line 398, in load_view_str
return _view_from_dict(root_view_dict, g, l, verbose=verbose)
File "/var/containers/Bundle/Application/E4751F4F-64A4-4BE6-AB9D-9C9564715002/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/ui.py", line 379, in _view_from_dict
subview = _view_from_dict(d, f_globals, f_locals, verbose=verbose)
File "/var/containers/Bundle/Application/E4751F4F-64A4-4BE6-AB9D-9C9564715002/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/ui.py", line 321, in _view_from_dict
_bind_action(v, attrs.get('action'), f_globals, f_locals, verbose=verbose)
TypeError: my_bind_action() got an unexpected keyword argument 'verbose'
@cvp Maybe you changed some parts of the code? I'm sure there is no such serious bug.
@lpl no change at all, but I have the new Pythonista beta...
@lpl could you tell me where is the code which is executed when you scroll your indexes
@cvp I'm using the beta too. And I shared the code to my friends. No such issues before… so strange
@cvp https://github.com/yjqiang/yj_ebook_reader/blob/master/index_viewer/index_viewer.py#L107
@lpl sure your req_data does not access internet?
Is it possible to replace your buttons by labels, title->text, only for scrolling, not for clicking, just to see if speed better?
@cvp Did you notice "bg"? This means backgroud, and it just send requests. And I use load_data to load the result into view.
@lpl ok, sorry
Do you think it is possible to replace buttons by labels if you don't tap them?
@cvp I compared before, and no white parts with Label.
@lpl First, I restarted Pythonista and the error disappears, it was in module ui.py???
Could you try to comment the line where you set dynamically the action of the button
@lpl As I don't understand the displayed language, could you tell me what I have to do to get the indexes list?
Edit: found what I have to tap: menu top right/3rd blue line
@cvp Sure. https://github.com/yjqiang/yj_ebook_reader/blob/master/index_viewer/index_viewer.py#L109 I use self.cur_offset and the new offset to check whether users are scrolling down or up.
If scrolling down(the scrollbar is going down), I use https://github.com/yjqiang/yj_ebook_reader/blob/master/index_viewer/index_viewer.py#L122 to handle this. If the lastest part of the subviews is done (https://github.com/yjqiang/yj_ebook_reader/blob/master/index_viewer/index_viewer.py#L126), I will move the first part of the subviews to the end of them(https://github.com/yjqiang/yj_ebook_reader/blob/master/index_viewer/index_viewer.py#L137-L141)
@lpl if I comment all the lines where you change .title, you are right, no blank...
@lpl only line 137 needs to be commented and white disappears...
@cvp But the function is broken… If you delete L137
@lpl I understand, it is only to show from which line the problem comes. It is not yet a solution...
@cvp So the performance of button.title is not good.
@lpl It seems, yes. Thus you were right from the beginning 😀
@omz Can you check this? And thank you @cvp . You are so patient.
@lpl Really not sure that could help, but you could try to add a label as subview of the button, with transparent background so the button stays clickable, and set the label.text instead of the button.title
@cvp said:
@lpl Really not sure that could help, but you could try to add a label as subview of the button, with transparent background so the button stays clickable, and set the label.text instead of the button.title
I'll have a try. And thx again.
@lpl The problem is not for @omz. It seems it comes from animation of UIButton when its title changes
@cvp said:
@lpl The problem is not for @omz. It seems it comes from animation of UIButton when its title changes
Maybe he should give an api to control this? I don't know. But I want to hear his advice.
@lpl, do you have a compelling reason to use a button instead of a custom View with touch_ended? Custom view will also let you do whatever you want with images and labels.
@mikael I agree, this not very different than a button without title and a label as subview....
@mikael I didn't try a custom View before. You mean I can make something like Button or Label? And push these things into the ScrollView?
@lpl I am estonished (of) my-self but after hours of search and tests,, I have been able to solve your problem with Objective-c.
That uses the fact that ui.Button is not a real UIButton but has it as subview, and also uses a block to execute code outside the animation process of an UIButton...
Replace index_viewer.py by
from collections import deque
import ui
import console
from objc_util import *
UIButton = ObjCClass('UIButton')
class IndexViewer:
ITEM_H = 32
LEN_LINE = 19
LOADING = 'LOADING...'
def __init__(self, parent):
reader_view = ui.load_view('index_viewer/index')
scrollview = reader_view['scrollview']
for i in range(19):
button = reader_view[f'button{i}']
scrollview.add_subview(button)
button.action = self.open_url
self.has_sent_req = False
self.scrollview = scrollview
self.reader_view = reader_view
scrollview.delegate = self
self.items = deque(self.scrollview.subviews)
assert (len(self.items) - 1) * self.ITEM_H > scrollview.height
self.parent = parent
def open_url(self, sender):
if sender.url is not None:
self.parent.open_url(sender.url)
console.hud_alert(sender.url)
def req_data_bg(self):
self.parent.req_data_bg(self)
def req_data(self, init=False):
self.parent.req_data(self, init)
def set_navi_view_name(self, name):
self.parent.set_navi_view_name(name)
def refresh_title(self):
pass
def load_data(self):
element = self.parent.load_data(self)
if element is None:
return None
if element[0] is None:
if not element[2]:
# 到底之后不再复位self.has_sent_req
console.hud_alert('已经阅读完毕')
return None
chapter, title, init = element
sum_num_lines = 0 if init else 1
if init:
self.set_navi_view_name(title)
for para in chapter:
sum_num_lines += 1
self.contents.append((para))
split_contents = '—' * self.LEN_LINE
self.contents.append((None, split_contents))
self.scrollview.content_size += (0, sum_num_lines * self.ITEM_H)
self.has_sent_req = False
return sum_num_lines
def reset_view(self, i=0, j=0):
scrollview = self.scrollview
# 这里把offset归零了,并调用了函数
scrollview.content_size = (scrollview.width, 0)
self.cur_offset = 0
self.contents = []
self.req_data(True)
sum_num_lines = self.load_data()
if sum_num_lines is None:
for item in self.items:
item.title = ''
item.url = None
self.items[0].title = '无目录'
self.items[0].y = 0
return
# 其实应该仿照img模块的,但是没必要,白白把逻辑弄麻烦了
while sum_num_lines <= len(self.items):
self.req_data()
sum_num_lines += self.load_data()
i = 0
y = 0
for item in self.items:
item.url, item.title = self.contents[i]
# i代表段落index,j代表了段落里面具体的文字起始下标
item.i = i
item.j = j
item.y = y
i += 1
y += self.ITEM_H
def reset_scrollbar(self):
pass
def get_UIButton(self,ui_button):
o = ObjCInstance(ui_button)
for subview in o.subviews():
if subview.isKindOfClass(UIButton):
self.UIButton = subview
break
def handler(self,_cmd,obj1_ptr):
self.UIButton.title = self.UIButton_title
def scrollview_did_scroll(self, scrollview):
offset = scrollview.content_offset.y
is_scroll_down = True if offset > self.cur_offset else False
self.cur_offset = offset
# print('t')
reader_h = scrollview.height
# print('t')
content_size = scrollview.content_size[1]
# 预加载
if content_size and content_size - self.cur_offset <= 3.5 * reader_h:
self.req_data_bg()
# 滚动条下移
if is_scroll_down:
while True:
item_end = self.items[-1]
item_start = self.items[0]
if item_end.y + self.ITEM_H -reader_h < offset and item_end.i is not None:
i = item_end.i + 1
if i >= len(self.contents):
title = self.LOADING
self.req_data_bg()
i = None
url = None
else:
url, title = self.contents[i]
#============================== begin
#item_start.title = title
self.get_UIButton(item_start)
self.UIButton_title = title
block = ObjCBlock(self.handler, restype=None, argtypes=[c_void_p, c_void_p])
UIButton.animateWithDuration_animations_(0,block)
#============================== end
item_start.y = item_end.y + self.ITEM_H
item_start.i = i
item_start.url = url
self.items.rotate(-1)
elif item_end.i is None and item_end.y <= offset + reader_h:
if self.load_data() is None:
break
item = self.items[-2]
i = item.i + 1
j = 0
# 由于一定会补足间隔行,所以这里i一定不越界
text = self.contents[i][0][j: j + self.LEN_LINE]
item_end.text = text
item_end.y = item.y + self.ITEM_H
item_end.i = i
item_end.j = j
else:
break
else:
while True:
item_end = self.items[-1]
item_start = self.items[0]
if item_start.y >= offset:
i = item_start.i
if i is None or i <= 0:
break
i = i - 1
url, text = self.contents[i]
#============================== begin
#item_end.title = text
self.get_UIButton(item_end)
self.UIButton_title = text
block = ObjCBlock(self.handler, restype=None, argtypes=[c_void_p, c_void_p])
UIButton.animateWithDuration_animations_(0,block)
#============================== end
item_end.y = item_start.y - self.ITEM_H
item_end.i = i
item_end.url = url
self.items.rotate(1)
else:
break
self.refresh_title()
'''
controller = Controller(EBookLoader, IndexViewer)
controller.load_reader(url)
controller.navi_viewer.view.present('fullscreen')
'''
@lpl, yes, that is what I meant.
I hate obj-c. If I have to do every thing with obj-c. Please change the name pythonista into objcista. @omz Would you please check the button.title ?
@lpl, but if you just use a custom view with a label, you do not need any ObjC.
@mikael Yes, I'd like to try this.
@lpl, for your inspiration, here is a ”buttonlike” that apes the necessary parts of the Button API to act as a drop-in replacement.
#coding: utf-8
from ui import *
class Buttonlike(View):
def __init__(self, **kwargs):
self.label = Label(frame=self.bounds, flex='WH', alignment=ALIGN_CENTER)
self.add_subview(self.label)
super().__init__(**kwargs)
@property
def title(self):
return self.label.text
@title.setter
def title(self, value):
self.label.text = value
@property
def tint_color(self):
return self.label.text_color
@tint_color.setter
def tint_color(self, value):
self.label.text_color = value
def touch_ended(self, touch):
if hasattr(self, 'action') and callable(self.action):
self.action(self)
if __name__ == '__main__':
v = Buttonlike(title='Hello world')
v.background_color = 'white'
v.tint_color = 'black'
def action_handler(sender):
sender.title = 'Clicked'
v.action = action_handler
v.present()