Forum Archive

The performance of button.name is kind of slow?

[deleted]

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.

cvp

@lpl Are you sure you don't want to use button.title?

[deleted]

@cvp changed.

cvp

@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.

[deleted]

https://m.youtube.com/watch?v=vqxlf4iJk20&feature=share

[deleted]

@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.

cvp

@lpl yes, for the labels, but how much indexes?

[deleted]

@cvp 13 Buttons, too. The index may very big, too?

cvp

@lpl I have a TableView with 10000 lines, very quick. Don't forget that display lines is done only when needed

[deleted]

@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.

cvp

@lpl Not textview but TableView and you can display what you want, like images, in each line.

The covers of my 13000 ePubs

[deleted]

@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.

[deleted]

@cvp But I need to display the full images on the screen which may have different sizes.

cvp

@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

[deleted]

@cvp Sorry. I wrote another word. I understood that, but I'm not a native speaker so sometimes I may wrote the wrong word.

[deleted]

@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?

[deleted]

@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?

cvp

@lpl Not easy to help without the code

[deleted]

@cvp https://github.com/yjqiang/yj_ebook_reader

Run eimg_reader.py or ebook_reader.py, and be careful, there are some 18+ contents.

[deleted]

@cvp I can use telegram to chat, if you often uses it too.

cvp

@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?

[deleted]

@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.

[deleted]

@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.

cvp

@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

cvp

@lpl try your buttons without image, just to check

[deleted]

@cvp pip install toml I used toml module in my code. Should write a requirement file.

cvp

@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

[deleted]

@cvp No, I loaded all and then I display them.

cvp

@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'
[deleted]

@cvp Maybe you changed some parts of the code? I'm sure there is no such serious bug.

cvp

@lpl no change at all, but I have the new Pythonista beta...

cvp

@lpl could you tell me where is the code which is executed when you scroll your indexes

[deleted]

@cvp I'm using the beta too. And I shared the code to my friends. No such issues before… so strange

[deleted]

@cvp https://github.com/yjqiang/yj_ebook_reader/blob/master/index_viewer/index_viewer.py#L107

cvp

@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?

[deleted]

@cvp Did you notice "bg"? This means backgroud, and it just send requests. And I use load_data to load the result into view.

cvp

@lpl ok, sorry
Do you think it is possible to replace buttons by labels if you don't tap them?

[deleted]

@cvp I compared before, and no white parts with Label.

cvp

@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

cvp

@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

[deleted]

@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)

cvp

@lpl if I comment all the lines where you change .title, you are right, no blank...

cvp

@lpl only line 137 needs to be commented and white disappears...

[deleted]

@cvp But the function is broken… If you delete L137

cvp

@lpl I understand, it is only to show from which line the problem comes. It is not yet a solution...

[deleted]

@cvp So the performance of button.title is not good.

cvp

@lpl It seems, yes. Thus you were right from the beginning 😀

[deleted]

@omz Can you check this? And thank you @cvp . You are so patient.

cvp

@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

[deleted]

@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.

cvp

@lpl The problem is not for @omz. It seems it comes from animation of UIButton when its title changes

[deleted]

@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.

mikael

@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.

cvp

@mikael I agree, this not very different than a button without title and a label as subview....

[deleted]

@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?

cvp

@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')
'''
mikael

@lpl, yes, that is what I meant.

[deleted]

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 ?

mikael

@lpl, but if you just use a custom view with a label, you do not need any ObjC.

[deleted]

@mikael Yes, I'd like to try this.

mikael

@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()