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.
ui.Path(), Scene question regarding drawing circle segments
-
Alright, I’ve been asking a lot of you guys (well, mainly @JonB and @cvp haha).
However, I have one more question / request..I thought it would be something I could easily accomplish but I was wrong.
Was hoping to write a simple program that does the following:— Draw a dartboard on screen (circle split into 20 equal segments is all I need)
— This works out to be 20 segments that should be 18 degrees each (360/20)
— Set up each segment as a clickable button
— The button.Action will be to highlight the selected segment and place a number label on it
— For example:
— Tapping top center segment will highlight it and place number 1 in segment
— Then, if I tap a 2nd segment, it highlights it (same color as first is fine) and places number 2
— This continues for 3 total selections. However, more is fine.I keep running into issues doing this.. It could be my lack of Geometry skills, or that I haven’t used Path() before, or maybe I’m just not cut out for programming ;)
What is the best way to go about doing this?
I’ve looked for code and tried reusing a few, but I end up with a weird pie chart that doesn’t ‘slice’ where I expect it to. Or I end up with some pretty cool drawings, from an abstract point of view! -
Quick and dirty (if I correctly understood your request)
Check script, you will see, no need of buttons
Tap -> angle -> segment
Of course, you could change color as each segment
ui.set_color((1-0.02*self.n,0,0,1)) => different redsEnd of the day for me, sorry, noT free until tomorrow, good luck
from gestures import * from math import pi,cos,sin,atan2 import ui class MyView(ui.View): def __init__(self): self.background_color = 'white' iv = ui.ImageView(name='iv') self.r = 400 iv.frame = (10,10,self.r*2,self.r*2) iv.background_color = self.background_color self.add_subview(iv) tap(iv,self.tap_handler) with ui.ImageContext(self.r*2,self.r*2) as ctx: path = ui.Path.oval(0,0,self.r*2,self.r*2) ui.set_color('lightgray') path.fill() ui.set_color('black') for i in range(20): a = -i*18*pi/180 x = self.r*(1+cos(a)) y = self.r*(1+sin(a)) path.move_to(self.r,self.r) path.line_to(x,y) path.stroke() iv.image = ctx.get_image() self.n = 0 def tap_handler(self,data): #print('tap_handler:',data.state,data.view) x,y = data.location dx = x - self.r dy = y - self.r a = atan2(-dy,dx)*180/pi a = (a+360) % 360 s = int(a/18) # segment 0 to 19 self.n += 1 with ui.ImageContext(self.r*2,self.r*2) as ctx: self['iv'].image.draw(0,0,self.r*2,self.r*2) path = ui.Path() a = -s*18*pi/180 x = self.r*(1+cos(a)) y = self.r*(1+sin(a)) path.move_to(self.r,self.r) path.line_to(x,y) path.add_arc(self.r, self.r, self.r, a, a-18*pi/180, False) path.line_to(self.r,self.r) ui.set_color('red') path.fill() a = a-9*pi/180 x = self.r*(1+0.5*cos(a)) y = self.r*(1+0.5*sin(a)) ui.draw_string(str(self.n),rect=(x,y,20,10),font=('Menlo',10)) self['iv'].image = ctx.get_image() def main(): global main_view main_view = MyView() main_view.present('fullscreen',hide_title_bar=False) if __name__ == '__main__': main()
-
@cvp
That’s perfect! Thank you a ton haha. I see that I was close on what I had..
I will put what you have to good use, thank you!I just couldn’t understand why ‘18’ wouldn’t work and that ‘9’ was producing better looking results.
But now that you gave me something that works, I can finally understand where I went wrong.
Very much appreciated! -
@cvp
Oh, whatcha got in your gestures module? Haha. That is not included by default:p -
@Robert_Tompkins said:
whatcha got in your gestures module?
Oh yes, sorry, it became a standard for me.
It is a marvelous module of our marvelous @mikael, and you will find it here -
@Robert_Tompkins said:
‘9’ was producing better looking results.
9° is to find the middle of the slice and draw there the number
-
@Robert_Tompkins and I also forgot (now you understand my "dirty") to check if you tap inside the circle or not
def tap_handler(self,data): #print('tap_handler:',data.state,data.view) x,y = data.location if ((x-self.r)**2 + (y-self.r)**2) > self.r**2: # tap outside the circle, no action return
-
def main(): global main_view main_view = MyView() main_view.present('fullscreen', hide_title_bar=False) if __name__ == '__main__': main() # Could be simplified as... MyView().present()
-
@ccc said:
MyView().present()
Not true, my friend. Since ios13 (not sure), you need the word 'fullscreen'.
Try with and without to see the difference.Édit: I like to put the hide_title_bar, True or not, so if you want to change, easier. But that's personal.
-
you need the word 'fullscreen'
iPad or iPhone or both?
-
@ccc I only use my iPad for Pythonista. I remember there was a topic about this problem...
But don't ask me more. -
@ccc no parameter => "full_screen", not "fullscreen", different views.
-
@cvp
Alright, I installed the version of stash mentioned in his repo.
Then conveniently used pip to grab the gestures library to make sure I didn’t mess anything up.Now the dartboard works like a charm! Thanks again.
I’ll play around with it and see if I can clean it up without breaking it!
I really need to browse git for useful Pythonista modules.In a week or so I will be back onto the DatePicker program and understanding objC usage in what you gave me there haha. Slowly but surely..
-
-
@Robert_Tompkins for the fun...rotated numbers
from objc_util import * . . . #ui.draw_string(str(self.n),rect=(x,y,20,10),font=('Menlo',10)) l = ui.Label() l.frame = (x,y,20,10) l.font = ('Menlo',10) l.text = str(self.n) self['iv'].add_subview(l) o = ObjCInstance(l) a = -a rot = CGAffineTransform(cos(a),-sin(a),sin(a),cos(a),0,0) o.transform = rot self['iv'].image = ctx.get_image()
-
@Robert_Tompkins just discovered that for up and bottom center slices, lines are hidden by filling.
Thus small correction
#path.add_arc(self.r, self.r, self.r, a, a-18*pi/180, False) path.add_arc(self.r, self.r, self.r, a-0.005, a-18*pi/180+0.005, False)
-
Ooo awesome. Thanks! I have been playing around with it trying to figure out a way to o’offwet’ the starting point so that the center of the slice on each vertical and horizontal axes line up. (Like on a dartboard).
Let me see if I can throw an image here to show you what I mean.
-
@Robert_Tompkins not sure that I understand correctly, but try these 3 modified lines
from gestures import * from math import pi,cos,sin,atan2 from objc_util import * import ui class MyView(ui.View): def __init__(self): self.background_color = 'white' iv = ui.ImageView(name='iv') self.r = 400 iv.frame = (10,10,self.r*2,self.r*2) iv.background_color = self.background_color self.add_subview(iv) tap(iv,self.tap_handler) with ui.ImageContext(self.r*2,self.r*2) as ctx: path = ui.Path.oval(0,0,self.r*2,self.r*2) ui.set_color('lightgray') path.fill() ui.set_color('black') for i in range(20): a = -i*18*pi/180+(9*pi/180) # modified x = self.r*(1+cos(a)) y = self.r*(1+sin(a)) path.move_to(self.r,self.r) path.line_to(x,y) path.stroke() iv.image = ctx.get_image() self.n = 0 def tap_handler(self,data): #print('tap_handler:',data.state,data.view) x,y = data.location if ((x-self.r)**2 + (y-self.r)**2) > self.r**2: # tap outside the circle, no action return dx = x - self.r dy = y - self.r a = atan2(-dy,dx)*180/pi + 9 # modified a = (a+360) % 360 s = int(a/18) # segment 0 to 19 self.n += 1 with ui.ImageContext(self.r*2,self.r*2) as ctx: self['iv'].image.draw(0,0,self.r*2,self.r*2) path = ui.Path() a = -s*18*pi/180+(9*pi/180) # modified x = self.r*(1+cos(a)) y = self.r*(1+sin(a)) path.move_to(self.r,self.r) path.line_to(x,y) #path.add_arc(self.r, self.r, self.r, a, a-18*pi/180, False) path.add_arc(self.r, self.r, self.r, a-0.005, a-18*pi/180+0.005, False) path.line_to(self.r,self.r) ui.set_color((1,1,0,1)) path.fill() a = a-9*pi/180 x = self.r*(1+0.5*cos(a)) y = self.r*(1+0.5*sin(a)) #ui.draw_string(str(self.n),rect=(x,y,20,10),font=('Menlo',10)) l = ui.Label() l.frame = (x,y,20,10) l.font = ('Menlo',10) l.text = str(self.n) self['iv'].add_subview(l) o = ObjCInstance(l) a = -a rot = CGAffineTransform(cos(a),-sin(a),sin(a),cos(a),0,0) o.transform = rot self['iv'].image = ctx.get_image() def main(): global main_view main_view = MyView() main_view.present('fullscreen',hide_title_bar=False) if __name__ == '__main__': main()
-
@cvp
I assume he wants something likehttps://www.dimensions.com/element/dartboard
To make an darts scoring game.
So a central red circle, a larger central green circle (really an annular region), then annular slices from your segments. Two bug divisions radially of what you have already drawn, but with smaller radial cuts at the outside and in the middle. The thin radial segments alternate colors red and green, and the larger segments alternate yellow and black. See the dimensions to get the relative ratios.
I think your code could be modified to draw each segment using a inner and oute radius -- two arcs at different radii, and the lines end at the inner and outer radius, instead of going to the center.
The tap handling would need to take into account the radius, and not just angle
-
Actually what you have right there is perfect!
@JonB, I don’t need the little things! However, I will probably create a new version of this code and turn it into that for practice! I could definitely use it.
Edit: On second thought, that sounds real complicated. I might quit Python attempting that at this point :)@cvp Again, those changes are exactly what I needed, thanks!
(https://imgur.com/a/TiCqTRz)^^ Dunno how to do inline images haha. I ran out of ideas, so that’s a link to what I have with your changes.