# Making arcs and filling them with in ui.Path browsing

• posted
0

Hmmm, well this is embarrassing ðŸ˜°ðŸ˜±
I really can't figure out how to use the ui.Path functions to create arcs or filled segments for a circle. From what I can see I have to understand how to correctly use sin and cos at minimum to be able to do it. I looked it up on wiki tried, but was just getting a headache.

Would be fantastic if someone had time to write a small func or class so I can do this. I want it to make custom views / indicators. Like a progress indicator for a download etc. I want to do one like apples app updates where a thinker arc is drawn around a circle and another style where the segments are filled in. I think I need back the the params for Path.add_arc(center_x, center_y, radius, start_angle, end_angle[, clockwise=True]). Assume I have to pass a Rect and the required angle, hopefully 12 o'clock being 0 degrees.

• posted
0

An angle of 0 is "3 o'clock". The angles are also expected in radians, so you'll probably want to convert them from degrees first using `math.radians`.

(What I haven't quite figured out yet is how to stop an ugly line from going from (0, 0) to the start of the curve.)

• posted
0

@dgelessus , yes i seen 3 o'clock is 0 on wiki. But in the real world for real people I am sure we can offset that, so we can think in terms of a clock. I got so lost with these extra lines I almost created a Da Vinci illustration ðŸ˜‹ðŸ˜±ðŸ˜‹

• posted
0

The starting point will be at x_center + radiuscos(theta) , y_center +/- radius sin(theta)

where theta is the starting angle, and the sign on the y term depends on whether you are going counter clockwise (+) or clockwise (-)

a move_to this position before adding the arc should eliminate the line from the center.

posted
0

Below is a little example that draws a simple pie chart of the sort that's sometimes used for progress indicators. You simply pass the progress (between 0.0 and 1.0) to the `draw_pie()` function, and it'll return an image with a filled pie segment.

It would be pretty easy to use similar code in the `draw` method of a custom view instead of creating an image. Hope this helps.

``````import ui
from math import pi, sin, cos, radians

def draw_pie(p, r, fill_color='black'):
p = max(0.0, min(1.0, p))
with ui.ImageContext(r * 2, r * 2) as ctx:
ui.set_color(fill_color)
path = ui.Path()
center = ui.Point(r, r)
path.move_to(center.x, center.y)
end = start + p * radians(360)
path.close()
ui.set_color(fill_color)
path.fill()
return ctx.get_image()

pie_img = draw_pie(0.75, 200)
pie_img.show()
``````

• posted
0

@omz , works perfectly thanks. These are the times I wished I had a proper education.
Will share it later. Just fixing up the test class.

• posted
0

@omz , is there a way to get a reference to the ImageContext for a custom view class? I would like to be able to write to the ImageContext in a function outside the class if possible. I thought self.get_image() might have done it. But it doesn't

posted
0

@Phuket2 To use the `draw_pie` function in the `draw` method of a custom view, just remove the `ImageContext` entirely, like this:

``````def draw_pie(p, r, fill_color='black'):
p = max(0.0, min(1.0, p))
ui.set_color(fill_color)
path = ui.Path()
center = ui.Point(r, r)
path.move_to(center.x, center.y)
end = start + p * radians(360)
path.close()
ui.set_color(fill_color)
path.fill()
``````

You can then just call it as-is in the `draw` method of a custom view, e.g.:

``````class PieView (ui.View):
def __init__(self, *args, **kwargs):
ui.View.__init__(*args, **kwargs)
self._progress = 0.0

@property
def progress(self):
return self._progress

@progress.setter
def progress(self, value):
self._progress = value
self.set_needs_display()

def draw(self):
draw_pie(self.progress, self.width*0.5)
``````

If you also want to use this for creating images (outside of a view), you can simply wrap it like this:

``````def draw_pie_image(p, r, fill_color='black'):
with ui.ImageContext(r*2, r*2) as ctx:
draw_pie(p, r, fill_color)
return ctx.get_image()
``````

• posted
0

@omz , thanks. Just gives different options for design. To inherit from a class and override draw or to point to a function or class that will draw

• posted
0

@omz , oh! Just found out I can not use another custom view to embed my draw method in, which makes sense. But back to the first question then. Is there a way to get/set the ImageContext object. I can see I can return a ImageContext but would be nicer if I can just explicitly set or use an existing ImageContext

Equivalent of
MyImageContext = v.xxx
With MyImageContext as current:
Drawing cmds going to this view

posted
0

oh! Just found out I can not use another custom view to embed my draw method in, which makes sense.

It should be possible to do that. What does your code look like?

Is there a way to get/set the ImageContext object.

Not really. An `ImageContext` is basically just a way to redirect drawing code into an image (for example, to save a drawing as a file). You don't really need an `ImageContext` for drawing in custom views. Views have a drawing context of their own, but it doesn't correspond to an `ImageContext` object, and you can't really access it (nor should you actually need to).

When the `draw()` method (of a custom view) is called (by the system), a drawing context is already set up, and all drawing functions affect the current context. So it's perfectly fine to do something like this:

``````# ...
def draw(self):
SomeOtherViewClass.draw(self)
``````

to use the drawing functionality of a different view class.

• posted
0

@omz , thanks. You are right it does work. Not worth posting the code now, it's all over the place. Trying to work around many issues. But this will help simply a lot. Thanks again ðŸ˜±

• posted
0

@omz , it's still crap now. But but i think it can go somewhere. Trying to work on a test container as well as a type of framework for a widget/gadget/whatever....ðŸ˜±

It's basic...but still trying, gist is here

• posted
0

@omz The default `ui.View` doesn't seem to have a `draw` method. I assume this is special-cased internally to not cause errors, but it would be nice to have a default `draw` method (even if empty) so we can always use `super` in the `draw` method.

For example, this code currently fails, because `ui.View` doesn't have a `draw` method:

``````import ui

class CustomView(ui.View):
def draw(self):
super().draw()
ui.Path.rect(5, 5, 45, 45).fill()
``````

But if we'd write a subclass of `CustomView`, we would need to call `super().draw()`, otherwise the custom drawing code in `CustomView.draw` wouldn't be executed.

This isn't a big issue - currently you just have to leave out the `super` call when the base class is `ui.View` - but I am a fan of consistency. `:)`

• posted
0

@dgelessus , I am not sure I follow you 100% here. But in this case wouldn't you just do something like
If v.superview:
Do something...

I know your statement was directed at @omz , but just curious

• posted
0

@Phuket2 I'm not talking about superviews of a view, but about the superclass (base class) of a custom view class. What I mean is that currently when you subclass `ui.View` directly, you cannot call `super().draw()`, but when you subclass another custom view class (that has a custom `draw` method), you have to call `super().draw()`. If `ui.View` had an empty `draw` method, you could always call `super().draw()` no matter what.

• posted
0

@dgelessus , I don't have a fraction of your knowledge. I knew you were not talking about superviews directly. I only mention that, because what I understand is if there is not superview it's the root view object.
But I have read some docs on Python objects. I can't remember exactly what it says, but I think it's something like there is always a base class. I think it said something like that.
So, I guess you are saying when super is called on the base class it should point to itself , just to be consistent and compatible will other Python patterns.
Hmmm, I am already regretting this post. I am in the deep end of the pool and need floaties ðŸ˜±
Look if I am so wrong, it's ok. No need to waste your time to respond.

• posted
0

Yes, if you write a class and don't set any superclass(es) yourself, its superclass is automatically `object`. (In Python 3 at least - the situation is more complicated in Python 2.)

This isn't really important here though. I mean that `ui.View` should have a `draw` method, so that subclasses can use `super().draw()` in their own `draw` methods. (Currently this fails sometimes because `ui.View.draw` doesn't exist.)

• posted
1

@dgelessus , ok I think I see. Actually draw also does not exist in a custom class unless you have a draw method. I was just doing some print dir(obj) to understand your post.
If you print dir(custom_class) without a draw method defined it does not show up. So I guess it's just added at runtime as required. Not sure that's normal or not. I would have thought the method would be there, but not the ImageContext etc...not sure if it's normal or not . But seems like maybe @omz took a logic short cut ðŸ˜±ðŸ˜ˆðŸ˜ˆðŸ˜ˆ

Sure I will regret this post also, I am probably way off course. I will learn to keep my big mouth shut one day.

Internal error.

Oops! Looks like something went wrong!