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