更改猕猴桃的画布绘制顺序

问题描述 投票:-1回答:1

我的项目中有一个图像小部件,我继续根据触摸输入将Line对象添加到其画布(这是一个只有图像背景的简单绘图应用程序)。但是,有时,我会在屏幕上进行一些更改(长话短说,实际上是一个滚动视图,其中包含一个包含图像的框布局,并且我在运行时向其中添加了更多图像以制作无限图像),以及在屏幕上消失。我检查并注意到它们仍在画布的子代列表中,但没有显示在屏幕上。但是,我仍然能够画出更多的线。什么会导致这种行为?我什至尝试从仍然显示在屏幕上的旧Line()对象重绘,但仍然没有任何反应...

以下是相关代码:

python


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)

kv:

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
python kivy kivy-language
1个回答
0
投票

一种解决方法是在ImagesBoxLayout)的父级上画线。并且由于Images被添加到BoxLayout的底部,因此必须调整线的y坐标,因为在其下方添加了新的Images

这是您的MyImage类的修改版本,其执行此操作:

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)

要使用此功能,请修改您的MyScrollView的一部分:

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()
© www.soinside.com 2019 - 2024. All rights reserved.