Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
draw ui.Path within the coordinate system of a node
-
A
ShapeNode
is basically just aSpriteNode
that automatically creates itsTexture
using a path. The code is pretty simple, and you can actually look at it yourself by opening "Modules & Templates/Standard Library/site-packages/scene.py".If you want full control over the resulting texture's size, you could simply use a vanilla
SpriteNode
and draw the shape yourself.import ui, scene def texture_from_path(path, fill_color, width, height): with ui.ImageContext(width, height) as ctx: ui.set_color(fill_color) path.fill() img = ctx.get_image() return scene.Texture(img)
This is simplified a bit, but it shows the basic approach. You can look at
ShapeNode
's source code if you need its support of shadows, outlines and such. -
Ah, okay, that does make sense now. I do have a follow up question though. Is it possible to access the points of a path after it has been created? edit: and with access I mean read them.
Cheers,
zipit -
@zipit No, that's not possible, you'd have to keep a reference to the points yourself.
-
Okay, thanks. I gave the thread a more meaningful title. I cannot provide a full example as my code relies on other stuff, but here is a snippet how I did solve the problem now (a bit clunky). Maybe it will help someone in the future. I wrote two comments so that the code does make some sense.
def draw_line(self): ''' ''' if self.line is not None: self.line.remove_from_parent() minx, miny = None, None path = ui.Path() path.line_width = 2 # self is a pythonista node. self.anchors are some phythonista node # objects that are children of self. We want to draw a line through all # these anchors within the coord system of self. for i, anchor in enumerate(self.anchors): p = anchor.position # get/update the lower left corner minimum minx, miny = (p.x if minx is None else min(minx, p.x), p.y if miny is None else min(miny, p.y)) if i == 0: path.move_to(p.x, -p.y) else: path.line_to(p.x, -p.y) # the offset(position) of our node has to be the lower left corner # point plus the center vector of our path self.line = ShapeNode(path, stroke_color='green', fill_color='transparent', position = (minx + path.bounds.w * .5, miny + path.bounds.h * .5), parent=self)
-
Hi, looking back to this example, I tried to play a bit and discovered what seems to be an issue:
from scene import * import time class MyScene (Scene): def setup(self): sx, sy = self.size.w, self.size.h self.red = ShapeNode(ui.Path.rect(0,0,sx,sy),parent=self,fill_color='red',position=(0,0),anchor_point=(0,0)) path = ui.Path() path.line_width = 3 path.move_to(0,0) path.line_to(100,0) #path.line_to(0,100) # Uncommenting this line modifies the drawing of both previous lines self.cyan = ShapeNode(path,parent=self.red,stroke_color='white',position=(0,0),anchor_point=(0,0)) if __name__ == '__main__': run(MyScene())
Removing the comment modifies how both previous lines are drawn!
I tried to understand but it is still unclear for me -
@mikeno, without running this, I think what you are seeing is that the commented line changes the bounding box of the whole path and thus the node, and as the node
position
defines the center position, the path seems to move to left. -
Hi Mikael, thx for replying but is there a way to avoid this?
-
@mikeno, do you need to use
scene
, or in other words, what are you trying to do? -
I just want to draw a filled polygone following numerous coordinates, I already managed to do it with ui and canvas but only scene gives me the possibility to use full screen
-
@mikeno try this, and to close, swipe down with two fingers
import ui class my(ui.View): def draw(self): w,h = ui.get_screen_size() path = ui.Path()#.rect(0,0,w,h) path.line_width = 3 ui.set_color('red') path.move_to(100,100) path.line_to(200,100) path.line_to(100,200) path.close() path.fill() #path.stroke() v = my() v.present('fullscreen',hide_title_bar=True)
-
Yes thx it works but it doesn’t with several polygons
-
... and then, I need to redraw in order to see the polygons in a different zoom factor. As I wrote, I already tried with ui but I stoped because I could not redraw, that’s why I tried with scene
-
@mikeno said:
it doesn’t with several polygons
import ui class my(ui.View): def draw(self): w,h = ui.get_screen_size() path1 = ui.Path() path1.line_width = 3 ui.set_color('red') path1.move_to(100,100) path1.line_to(200,100) path1.line_to(100,200) path1.close() path1.fill() path2 = ui.Path()#.rect(0,0,w,h) path2.line_width = 3 ui.set_color('blue') path2.move_to(300,100) path2.line_to(400,100) path2.line_to(300,200) path2.close() path2.fill() v = my() v.present('fullscreen',hide_title_bar=True)
-
-
Thx to both of you, concerning the gestures, it seems to be a little bit complicated but I will try
-
My last issue is that the drawing doesn’t refresh even when I call explicitly drawTest() as you can see in the example below:
import ui class my(ui.View): def __init__(self, *args, **kwargs): self.factor = .5 def draw(self): print('draw()') self.drawTest() def drawTest(self): print('drawTest()') print('%.1f' % (self.factor)) w,h = ui.get_screen_size() path1 = ui.Path() path1.line_width = 3 ui.set_color('red') path1.move_to(100*self.factor,100*self.factor) path1.line_to(200*self.factor,100*self.factor) path1.line_to(100*self.factor,200*self.factor) path1.close() path1.fill() path2 = ui.Path()#.rect(0,0,w,h) path2.line_width = 3 ui.set_color('blue') path2.move_to(300*self.factor,100*self.factor) path2.line_to(400*self.factor,100*self.factor) path2.line_to(300*self.factor,200*self.factor) path2.close() path2.fill() def touch_began(self, touch): print('touch_began()') self.factor += .5 self.drawTest() def will_close(self): print('will_close()') v = my() v.present('fullscreen',hide_title_bar=True)
It redraws only when I turn the iPad, could you please tell me how I can force the refresh with the right zoom factor?
-
@mikeno start from
import ui class my(ui.View): def __init__(self, *args, **kwargs): self.w,self.h = ui.get_screen_size() iv = ui.ImageView(name='iv') iv.frame = (0,0,self.w,self.h) self.add_subview(iv) self.factor = .5 self.update_interval = 1 def update(self): with ui.ImageContext(self.w,self.h) as ctx: path1 = ui.Path() path1.line_width = 3 ui.set_color('red') path1.move_to(100*self.factor,100*self.factor) path1.line_to(200*self.factor,100*self.factor) path1.line_to(100*self.factor,200*self.factor) path1.close() path1.fill() path2 = ui.Path()#.rect(0,0,w,h) path2.line_width = 3 ui.set_color('blue') path2.move_to(300*self.factor,100*self.factor) path2.line_to(400*self.factor,100*self.factor) path2.line_to(300*self.factor,200*self.factor) path2.close() path2.fill() ui_image = ctx.get_image() self['iv'].image = ui_image def touch_began(self, touch): self.factor += .5 def will_close(self): print('will_close()') v = my() v.present('fullscreen',hide_title_bar=True)
-
Whao, I’m impressed, thank you!
But I don’t understand why it doesn’t work in my last example -
-
@mikeno just to show how it is easy to use @mikael 's gestures module, use two fingers to pinch
import ui from gestures import * class my(ui.View): def __init__(self, *args, **kwargs): self.w,self.h = ui.get_screen_size() iv = ui.ImageView(name='iv') iv.frame = (0,0,self.w,self.h) self.add_subview(iv) self.factor = .5 #self.update_interval = 1 self.pinch = pinch(iv,self.did_pinch) self.update() def update(self): with ui.ImageContext(self.w,self.h) as ctx: path1 = ui.Path() path1.line_width = 3 ui.set_color('red') path1.move_to(100*self.factor,100*self.factor) path1.line_to(200*self.factor,100*self.factor) path1.line_to(100*self.factor,200*self.factor) path1.close() path1.fill() path2 = ui.Path()#.rect(0,0,w,h) path2.line_width = 3 ui.set_color('blue') path2.move_to(300*self.factor,100*self.factor) path2.line_to(400*self.factor,100*self.factor) path2.line_to(300*self.factor,200*self.factor) path2.close() path2.fill() ui_image = ctx.get_image() self['iv'].image = ui_image def did_pinch(self, data): self.factor = data.scale self.update() def will_close(self): print('will_close()') def pinch_handler(data): print(data) v = my() v.present('fullscreen',hide_title_bar=True)