Python 更改kivy中的画布绘制顺序

Python 更改kivy中的画布绘制顺序,python,kivy,kivy-language,Python,Kivy,Kivy Language,我的项目中有一个图像小部件,我一直根据触摸输入(这是一个只有图像背景的简单绘图应用程序)向画布添加线条对象。然而,在某个时刻,我改变了屏幕上的某些内容(长话短说,它实际上是一个包含图像的boxlayout的滚动视图,我在运行时向它添加了更多图像以生成无限图像),屏幕上的线条消失了。我检查并注意到它们仍然在画布的子列表中,但只是没有显示在屏幕上。然而,我仍然能够画出更多的线。什么会导致这种行为?我甚至尝试重新绘制旧的Line()对象,从它们仍然显示在屏幕上时开始,但仍然没有发生任何事情 以下是相关

我的项目中有一个图像小部件,我一直根据触摸输入(这是一个只有图像背景的简单绘图应用程序)向画布添加线条对象。然而,在某个时刻,我改变了屏幕上的某些内容(长话短说,它实际上是一个包含图像的boxlayout的滚动视图,我在运行时向它添加了更多图像以生成无限图像),屏幕上的线条消失了。我检查并注意到它们仍然在画布的子列表中,但只是没有显示在屏幕上。然而,我仍然能够画出更多的线。什么会导致这种行为?我甚至尝试重新绘制旧的Line()对象,从它们仍然显示在屏幕上时开始,但仍然没有发生任何事情

以下是相关代码:

蟒蛇


class NotebookScreen(GridLayout):

    def __init__(self, **kwargs):
        global main_screen
        self.rows = 1
        super(NotebookScreen, self).__init__(**kwargs)
        self.bind(pos=self.update_notebook, size=self.update_notebook, on_touch_up=self.release_touch_func)

    def arrow_up_on_press(self):
        global scroll_up_event
        if scroll_up_event is not None:
            scroll_up_event.cancel()
            scroll_up_event = None
        scroll_up_event = Clock.schedule_interval(self.scroll_up, 0.1)

    def arrow_down_on_press(self):
        global scroll_down_event
        if scroll_down_event is not None:
            scroll_down_event.cancel()
            scroll_down_event = None
        scroll_down_event = Clock.schedule_interval(self.scroll_down, 0.1)

    def arrow_down_on_release(self):
        global scroll_down_event
        if scroll_down_event is not None:
            scroll_down_event.cancel()
            scroll_down_event = None

    def arrow_up_on_release(self):
        global scroll_up_event
        if scroll_down_event is not None:
            scroll_up_event.cancel()
            scroll_up_event = None

    def scroll_down(self, arg):
        global scrolls
        scrl = main_screen.ids.notebook_scroll
        if scrl.scroll_y - get_scroll_distance()[0] > 0:
            scrl.scroll_y -= get_scroll_distance()[0]
            scrolls += get_scroll_distance()[1]
        else:
            offset = get_scroll_distance()[0] - scrl.scroll_y
            scrl.scroll_y = 0
            main_screen.ids.notebook_scroll.on_scroll_y(0, 0, offset=offset)

    def scroll_up(self, arg):
        global scrolls
        scrl = main_screen.ids.notebook_scroll
        if scrl.scroll_y + get_scroll_distance()[0] < 1.:
            scrl.scroll_y += get_scroll_distance()[0]
            scrolls -= get_scroll_distance()[1]
        else:
            scrl.scroll_y = 1

    def update_notebook(self, a, b, **kwargs):
        for child in self.ids.notebook_image.children:
            child.size = MyImage.get_size_for_notebook(child)

    def release_touch_func(self, a1, a2, **kwargs):
        global scroll_up_event, scroll_down_event
        if scroll_up_event is not None:
            scroll_up_event.cancel()
            scroll_up_event = None
        if scroll_down_event is not None:
            scroll_down_event.cancel()
            scroll_down_event = None


class MyScrollView(ScrollView):

    def __init__(self, **kwargs):
        super(MyScrollView, self).__init__(**kwargs)
    def on_scroll_y(self, instance, scroll_val, offset=0):
        global main_screen, gen_id, scrolls

        if self.scroll_y == 0.:  # < get_scroll_distance()[0]:
            box = main_screen.ids.notebook_image
            old_height = box.height
            old_pos_y = self.scroll_y
            new_image = MyImage()
            new_image.id = next(gen_id)
            box.add_widget(new_image)
            old_height = (len(main_screen.ids.notebook_image.children) - 1) * main_screen.ids.notebook_image.children[
                0].height
            self.scroll_y = new_image.height / (old_height + new_image.height) - offset * box.height / old_height
            print([child.id for child in list(main_screen.ids.notebook_image.children)])

        # redraw all text from earlier
        for image in main_screen.ids.notebook_image.children:
            image.draw_all_lines()

    def slider_change(self, s, instance, value):
        if value >= 0:
            # this to avoid 'maximum recursion depth exceeded' error
            s.value = value

    def scroll_change(self, scrlv, instance, value):
        scrlv.scroll_y = value


class MyImage(Image):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.lines = []
        self.line_coords = []
        self.line_objects = []

    def get_size_for_notebook(self, **kwargs):
        global img_size
        width, height = Window.size
        return width, (max(img_size[0] * height / width, height))

    def to_image(self, x, y):
        ''''
        Convert touch coordinates to pixels

         :Parameters:
            `x,y`: touch coordinates in parent coordinate system - as provided by on_touch_down()

         :Returns: `x, y`
             A value of None is returned for coordinates that are outside the Image source
        '''

        # get coordinates of texture in the Canvas
        pos_in_canvas = self.center_x - self.norm_image_size[0] / 2., self.center_y - self.norm_image_size[1] / 2.

        # calculate coordinates of the touch in relation to the texture
        x1 = x - pos_in_canvas[0]
        y1 = y - pos_in_canvas[1]

        # convert to pixels by scaling texture_size/source_image_size
        if x1 < 0 or x1 > self.norm_image_size[0]:
            x2 = None
        else:
            x2 = self.texture_size[0] * x1 / self.norm_image_size[0]
        if y1 < 0 or y1 > self.norm_image_size[1]:
            y2 = None
        else:
            y2 = self.texture_size[1] * y1 / self.norm_image_size[1]
        return x2, y2

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            current_touch = self.to_image(*touch.pos)
            self.add_to_canvas_on_touch_down((touch.x, touch.y))
            touch.ud['line'] = Line(points=[touch.x, touch.y])
            with self.canvas:
                Color(0, 0, 1, 1)
                l = Line(points=touch.ud['line'].points)
                self.line_objects.append(l)
            return True
        else:
            return super(MyImage, self).on_touch_down(touch)

    def on_touch_move(self, touch):
        if self.collide_point(*touch.pos):
            current_touch = self.to_image(*touch.pos)
            self.add_to_canvas_on_touch_move((touch.x, touch.y))
            touch.ud['line'].points += (touch.x, touch.y)
            with self.canvas:
                Color(0, 0, 1, 1)
                l = Line(points=touch.ud['line'].points)
                self.line_objects.append(l)
            return True
        else:
            return super(MyImage, self).on_touch_move(touch)

    def add_to_canvas_on_touch_down(self, point):
        with self.canvas:
            self.line_coords.append([point])
            self.lines.append([point[0], point[1]])

    def add_to_canvas_on_touch_move(self, point):
        with self.canvas:
            self.lines[-1].append(point[0])
            self.lines[-1].append(point[1])
            self.line_coords[-1].append(point)

    def draw_all_lines(self):
            with self.canvas.after:
                Color(0, 0, 1, 1)
                Line(points=line)

课堂笔记簿屏幕(网格布局):
定义初始(自我,**kwargs):
全局主屏幕
self.rows=1
超级(记事本屏幕,自我)。\uuuu初始化(**kwargs)
self.bind(pos=self.update\u notebook,size=self.update\u notebook,on\u touch\u up=self.release\u touch\u func)
def箭头向上按(自身):
全局向上滚动事件
如果向上滚动事件不是无:
向上滚动事件。取消()
向上滚动事件=无
向上滚动事件=时钟。计划间隔(self.scroll\u up,0.1)
def箭头向下按(自身):
全局滚动\u向下\u事件
如果向下滚动事件不是无:
向下滚动事件。取消()
向下滚动事件=无
向下滚动事件=时钟。计划间隔(self.scroll\u down,0.1)
def箭头向下打开释放(自):
全局滚动\u向下\u事件
如果向下滚动事件不是无:
向下滚动事件。取消()
向下滚动事件=无
def箭头在释放时向上(自):
全局向上滚动事件
如果向下滚动事件不是无:
向上滚动事件。取消()
向上滚动事件=无
def向下滚动(自身,参数):
全局卷轴
scrl=主屏幕.ids.notebook\u滚动
如果scrl.scroll\u y-get\u scroll\u distance()[0]>0:
scrl.scroll\u y-=获取滚动距离()[0]
滚动+=获取滚动距离()[1]
其他:
偏移量=获取滚动距离()[0]-scrl.scroll\u y
scrl.scroll_y=0
主屏幕.ids.notebook\u scroll.on\u scroll\u y(0,0,偏移量=偏移量)
def向上滚动(自身,参数):
全局卷轴
scrl=主屏幕.ids.notebook\u滚动
如果scrl.scroll\u y+获取\u scroll\u distance()[0]<1:
scrl.scroll_y+=获取滚动距离()[0]
滚动-=获取滚动距离()[1]
其他:
scrl.scroll_y=1
def更新记事本(自我、a、b、**kwargs):
对于self.ids.notebook_image.children中的子项:
child.size=MyImage.get\u大小\u笔记本(child)
def释放触摸功能(自身、a1、a2、**kwargs):
全局向上滚动事件,向下滚动事件
如果向上滚动事件不是无:
向上滚动事件。取消()
向上滚动事件=无
如果向下滚动事件不是无:
向下滚动事件。取消()
向下滚动事件=无
类MyScrollView(滚动视图):
定义初始(自我,**kwargs):
超级(MyScrollView,self)。\uuuuuuuuuuuuuuuuuuuu初始(**kwargs)
滚动时的定义(自身、实例、滚动值、偏移量=0):
全局主屏幕,gen\u id,滚动条
如果self.scroll_y==0.:#=0:
#这是为了避免“超过最大递归深度”错误
s、 价值=价值
def scroll_更改(自身、scrlv、实例、值):
scrlv.scroll_y=值
类MyImage(图像):
定义初始(自我,**kwargs):
超级()
self.lines=[]
self.line_coords=[]
self.line_对象=[]
def获取笔记本电脑的大小(自我,**kwargs):
全局img_大小
宽度、高度=窗口大小
返回宽度,(最大(img_尺寸[0]*高度/宽度,高度))
def到_图像(自身、x、y):
''''
将触摸坐标转换为像素
:参数:
`x、 y`:父坐标系中的触摸坐标-由on_touch_down()提供
:返回:`x,y`
对于图像源之外的坐标,将返回None值
'''
#获取画布中纹理的坐标
画布中的位置=self.center\u x-self.norm\u图像大小[0]/2.,self.center\u y-self.norm\u图像大小[1]/2。
#计算与纹理相关的触摸坐标
x1=x-画布中的位置[0]
y1=y-画布中的位置[1]
#通过缩放纹理大小/源图像大小转换为像素
如果x1<0或x1>self.norm\u图像大小[0]:
x2=无
其他:
x2=自纹理大小[0]*x1/自规范图像大小[0]
如果y1<0或y1>自身标准图像大小[1]:
y2=无
其他:
y2=自纹理大小[1]*y1/自规范图像大小[1]
返回x2,y2
def on_触控向下(自身,触控):
如果自碰撞点(*touch.pos):
MyScrollView:
                bar_color: [1, 0, 0, 1]
                id: notebook_scroll
                padding: 0
                spacing: 0
                do_scroll: (False, False)  # up and down
                BoxLayout:
                    padding: 0
                    spacing: 0
                    orientation: 'vertical'
                    id: notebook_image
                    size_hint: 1, None
                    height: self.minimum_height
                    MyImage:
                    MyImage:

<MyImage>:
    source: 'images/pic.png'
    allow_stretch: True
    keep_ratio: False
    size: root.get_size_for_notebook()
    size_hint: None, None
class MyImage(Image):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.line_coords = {}  # a dictionary with line object as key and coords as value

    def get_size_for_notebook(self, **kwargs):
        global img_size
        width, height = Window.size
        return width, (max(img_size[0] * height / width, height))

    def to_image(self, x, y):
        ''''
        Convert touch coordinates to pixels

         :Parameters:
            `x,y`: touch coordinates in parent coordinate system - as provided by on_touch_down()

         :Returns: `x, y`
             A value of None is returned for coordinates that are outside the Image source
        '''

        # get coordinates of texture in the Canvas
        pos_in_canvas = self.center_x - self.norm_image_size[0] / 2., self.center_y - self.norm_image_size[1] / 2.

        # calculate coordinates of the touch in relation to the texture
        x1 = x - pos_in_canvas[0]
        y1 = y - pos_in_canvas[1]

        # convert to pixels by scaling texture_size/source_image_size
        if x1 < 0 or x1 > self.norm_image_size[0]:
            x2 = None
        else:
            x2 = self.texture_size[0] * x1 / self.norm_image_size[0]
        if y1 < 0 or y1 > self.norm_image_size[1]:
            y2 = None
        else:
            y2 = self.texture_size[1] * y1 / self.norm_image_size[1]
        return x2, y2

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            with self.parent.canvas.after:
                Color(0, 0, 1, 1)
                touch.ud['line'] = Line(points=[touch.x, touch.y])

                # add dictionary entry for this line
                # save y coord as distance from top of BoxLayout
                self.line_coords[touch.ud['line']] = [touch.x, self.parent.height - touch.y]
            return True
        else:
            return super(MyImage, self).on_touch_down(touch)

    def on_touch_move(self, touch):
        if self.collide_point(*touch.pos):
            touch.ud['line'].points += (touch.x, touch.y)

            # save touch point with y coordinate as the distance from the top of the BoxLayout
            self.line_coords[touch.ud['line']].extend([touch.x, self.parent.height - touch.y])
            return True
        else:
            return super(MyImage, self).on_touch_move(touch)

    def draw_all_lines(self):
        with self.parent.canvas.after:
            Color(0, 0, 1, 1)
            for line, pts in self.line_coords.items():

                # create new list of points
                new_pts = []
                for i in range(0, len(pts), 2):
                    new_pts.append(pts[i])
                    # calculate correct y coord (height of BoxLayout has changed)
                    new_pts.append(self.parent.height - pts[i+1])

                # redraw this line using new_pts
                Line(points=new_pts)
def on_scroll_y(self, instance, scroll_val, offset=0):
    global main_screen, gen_id, scrolls

    if self.scroll_y == 0.:  # < get_scroll_distance()[0]:
        box = main_screen.ids.notebook_image
        old_height = box.height
        old_pos_y = self.scroll_y
        new_image = MyImage()
        new_image.id = next(gen_id)
        box.add_widget(new_image)
        old_height = (len(main_screen.ids.notebook_image.children) - 1) * main_screen.ids.notebook_image.children[
            0].height
        self.scroll_y = new_image.height / (old_height + new_image.height) - offset * box.height / old_height
        print([child.id for child in list(main_screen.ids.notebook_image.children)])

        # use Clock.schedule_once to do the drawing after heights are recalculated
        Clock.schedule_once(self.redraw_lines)

def redraw_lines(self, dt):
    # redraw all text from earlier
    self.ids.notebook_image.canvas.after.clear()
    for image in self.ids.notebook_image.children:
        image.draw_all_lines()