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.
Analog Clock Example to ui Example - help
-
I was interested in making a type of world display in ui. Of course a digital version not so challenging esp. with the new update method for custom views. I statered butchering omz's analog Example to get it to draw inside a custom ui.View class. I just don't understand the math well enough. I know basic for some, just not me. I am just guessing at the transformations I have to do. One thing I also should have done was use an ImageContext, I was getting to that.
Anyway, I thought maybe a member here a little bored on a Sunday with a few mins :) might be interested in doing the conversion properly. Maybe add a few properties for some of the colours/fonts etc...
I think if it can be written nice and clean to PEP8 @omz might consider adding it to his examples as it would show the use of the new update method, imagecontexts, custom drawing.
Besides that we get a neat Analog Clock class we can use. Anyway, was just an idea.
I have put my butchered code below along with @omz Example. I know it's crappy, just to give an idea the direction I was going. I think the overall direction is ok. It's just about getting the drawing done correctly. The datetime calcs probably should have their own method. As I say, I was just trying to see if I could convert it.
Oh, I think it's important that it's resizable. Eg, not just fullscreen. This way you can present many copies of the clock into custom views on a ui.View for example.
Ok, will be interesting to see if anyone takes up the challenge. I have a feeling if you know what you are doing this is around 10-15mins work. I wish I could just do it.
''' A simple analog clock made of ShapeNodes. ''' import ui from scene import * from math import pi, sin, cos from datetime import datetime class Clock2(ui.View): def __init__(self): self.width = 600 self.height = 600 self.hands = [(),(),()] self.update_interval = 1 def update(self): self.set_needs_display() #print('update') def draw(self): r = min(self.width, self.height)/2 * 0.9 circle = ui.Path.oval(0, 0, r*2, r*2) circle.line_width = 6 shadow = ('black', 0, 0, 15) ui.set_shadow(*shadow) ui.set_color('silver') #self.face = ShapeNode(circle, 'white', 'silver', shadow=shadow) circle.fill() circle.stroke() for i in range(12): label = LabelNode(str(i+1), font=('HelveticaNeue-UltraLight', 0.2*r)) label.color = 'black' a = 2 * pi * (i+1)/12.0 #label.position = sin(a)*(r*0.85), cos(a)*(r*0.85) label.position = sin(a)*(r*0.85), cos(a)*(r*0.85) #print(label.position) ui.draw_string(str(i+1), rect=(r+ label.position[0], r +label.position[1], 0, 0), font=('<system>', 18), color='black', alignment=ui.ALIGN_CENTER, line_break_mode=ui.LB_WORD_WRAP) #self.hands = [] #hand_attrs = [(r*0.6, 8, 'black'), (r*0.9, 8, 'black'), (r*0.9, 4, 'red')] #self.hands = [(r*0.6, 8, 'black'), (r*0.9, 8, 'black'), (r*0.9, 4, 'red')] t = datetime.now() tick = -2 * pi / 60.0 seconds = t.second + t.microsecond/1000000.0 minutes = t.minute + seconds/60.0 hours = (t.hour % 12) + minutes/60.0 self.hands[0] = 5 * tick * hours self.hands[1] = tick * minutes self.hands[2] = tick * seconds #print(type(self.hands)) for l, w, color in self.hands: #shape = ShapeNode(ui.Path.rounded_rect(0, 0, w, l, w/2), color) shape = ui.Path.rounded_rect(0, 0, w, l, w/2) #shape.anchor_point = (0.5, 0) shape.stroke() #self.hands.append(shape) #self.face.add_child(shape) class Clock (Scene): def setup(self): print(self.size) r = min(self.size)/2 * 0.9 circle = ui.Path.oval(0, 0, r*2, r*2) circle.line_width = 6 shadow = ('black', 0, 0, 15) self.face = ShapeNode(circle, 'white', 'silver', shadow=shadow) self.add_child(self.face) for i in range(12): label = LabelNode(str(i+1), font=('HelveticaNeue-UltraLight', 0.2*r)) label.color = 'black' a = 2 * pi * (i+1)/12.0 label.position = sin(a)*(r*0.85), cos(a)*(r*0.85) self.face.add_child(label) self.hands = [] hand_attrs = [(r*0.6, 8, 'black'), (r*0.9, 8, 'black'), (r*0.9, 4, 'red')] for l, w, color in hand_attrs: shape = ShapeNode(ui.Path.rounded_rect(0, 0, w, l, w/2), color) shape.anchor_point = (0.5, 0) self.hands.append(shape) self.face.add_child(shape) self.face.add_child(ShapeNode(ui.Path.oval(0, 0, 15, 15), 'black')) self.did_change_size() def did_change_size(self): self.face.position = self.size/2 def update(self): t = datetime.now() tick = -2 * pi / 60.0 seconds = t.second + t.microsecond/1000000.0 minutes = t.minute + seconds/60.0 hours = (t.hour % 12) + minutes/60.0 self.hands[0].rotation = 5 * tick * hours self.hands[1].rotation = tick * minutes self.hands[2].rotation = tick * seconds if __name__ == '__main__': is_scene_clock = False if is_scene_clock: run(Clock()) else: v = Clock2() v.present('sheet'''' A simple analog clock made of ShapeNodes. ''' import ui from scene import * from math import pi, sin, cos from datetime import datetime class Clock2(ui.View): def __init__(self): self.width = 600 self.height = 600 self.hands = [(),(),()] self.update_interval = 1 def update(self): self.set_needs_display() #print('update') def draw(self): r = min(self.width, self.height)/2 * 0.9 circle = ui.Path.oval(0, 0, r*2, r*2) circle.line_width = 6 shadow = ('black', 0, 0, 15) ui.set_shadow(*shadow) ui.set_color('silver') #self.face = ShapeNode(circle, 'white', 'silver', shadow=shadow) circle.fill() circle.stroke() for i in range(12): label = LabelNode(str(i+1), font=('HelveticaNeue-UltraLight', 0.2*r)) label.color = 'black' a = 2 * pi * (i+1)/12.0 #label.position = sin(a)*(r*0.85), cos(a)*(r*0.85) label.position = sin(a)*(r*0.85), cos(a)*(r*0.85) #print(label.position) ui.draw_string(str(i+1), rect=(r+ label.position[0], r +label.position[1], 0, 0), font=('<system>', 18), color='black', alignment=ui.ALIGN_CENTER, line_break_mode=ui.LB_WORD_WRAP) #self.hands = [] #hand_attrs = [(r*0.6, 8, 'black'), (r*0.9, 8, 'black'), (r*0.9, 4, 'red')] #self.hands = [(r*0.6, 8, 'black'), (r*0.9, 8, 'black'), (r*0.9, 4, 'red')] t = datetime.now() tick = -2 * pi / 60.0 seconds = t.second + t.microsecond/1000000.0 minutes = t.minute + seconds/60.0 hours = (t.hour % 12) + minutes/60.0 self.hands[0] = 5 * tick * hours self.hands[1] = tick * minutes self.hands[2] = tick * seconds #print(type(self.hands)) for l, w, color in self.hands: #shape = ShapeNode(ui.Path.rounded_rect(0, 0, w, l, w/2), color) shape = ui.Path.rounded_rect(0, 0, w, l, w/2) #shape.anchor_point = (0.5, 0) shape.stroke() #self.hands.append(shape) #self.face.add_child(shape) class Clock (Scene): def setup(self): print(self.size) r = min(self.size)/2 * 0.9 circle = ui.Path.oval(0, 0, r*2, r*2) circle.line_width = 6 shadow = ('black', 0, 0, 15) self.face = ShapeNode(circle, 'white', 'silver', shadow=shadow) self.add_child(self.face) for i in range(12): label = LabelNode(str(i+1), font=('HelveticaNeue-UltraLight', 0.2*r)) label.color = 'black' a = 2 * pi * (i+1)/12.0 label.position = sin(a)*(r*0.85), cos(a)*(r*0.85) self.face.add_child(label) self.hands = [] hand_attrs = [(r*0.6, 8, 'black'), (r*0.9, 8, 'black'), (r*0.9, 4, 'red')] for l, w, color in hand_attrs: shape = ShapeNode(ui.Path.rounded_rect(0, 0, w, l, w/2), color) shape.anchor_point = (0.5, 0) self.hands.append(shape) self.face.add_child(shape) self.face.add_child(ShapeNode(ui.Path.oval(0, 0, 15, 15), 'black')) self.did_change_size() def did_change_size(self): self.face.position = self.size/2 def update(self): t = datetime.now() tick = -2 * pi / 60.0 seconds = t.second + t.microsecond/1000000.0 minutes = t.minute + seconds/60.0 hours = (t.hour % 12) + minutes/60.0 self.hands[0].rotation = 5 * tick * hours self.hands[1].rotation = tick * minutes self.hands[2].rotation = tick * seconds if __name__ == '__main__': is_scene_clock = False if is_scene_clock: run(Clock()) else: v = Clock2() v.present('sheet')
-
Here is the code displaying four analog clocks (no scene functions, pure ui)
import ui from math import pi, sin, cos from datetime import datetime LabelNode = ui.Label SpriteNode = ui.ImageView class ShapeNode(ui.View): def __init__(self, path=None, fill_color='white', stroke_color='clear', shadow=None, *args, **kwargs): self.path = path self.fill_color = fill_color self.stroke_color = stroke_color super().__init__(*args, **kwargs) def draw(self): ui.set_color(self.fill_color) self.path.fill() ui.set_color(self.stroke_color) self.path.stroke() class AnalogClock(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) center_x, center_y = self.center self.w1 = min(self.height, self.width) center_x, center_y = (self.w1/2, self.w1/2) r = (self.w1/2) * 0.9 #print(r) circle = ui.Path.oval(0, 0, r*2, r*2) circle.line_width = 6 shadow = ('black', 0, 0, 15) frame = (center_x -r, center_y - r, 2*r, 2*r) self.face = ShapeNode(circle, 'white', 'silver', shadow=shadow, frame=frame ) self.add_subview(self.face) for i in range(12): a = 2 * pi * (i+1)/12.0 -pi/2 label = LabelNode(text='{:2d}'.format(i+1), font=('HelveticaNeue-UltraLight', 0.2*r), text_color='black', frame=(cos(a)*(r*0.85)+center_x-.1*r, sin(a)*(r*0.85)+center_y-r*.85, 2*r*.85, 2*r*.85)) self.add_subview(label) self.hands = [] self.update_interval = .1 hand_attrs = [(r*0.6, 8, 'black'), (r*0.9, 8, 'black'), (r*0.9, 4, 'red')] for l, w, color in hand_attrs: shape = ShapeNode(ui.Path.rounded_rect(l-w/2, 0, w, l, w/2), color, frame=(center_x-l, center_y-l, 2*l, 2*l)) #shape.anchor_point = (0.5, 0) self.hands.append(shape) self.add_subview(shape) self.add_subview(ShapeNode(ui.Path.oval(0, 0, 15, 15), 'black', frame=(center_x-7.5, center_y-7.5, 15, 15))) def update(self): t = datetime.now() tick = 2 * pi / 60.0 seconds = t.second + t.microsecond/1000000.0 minutes = t.minute + seconds/60.0 hours = (t.hour % 12) + minutes/60.0 self.hands[0].transform = ui.Transform.rotation(5 * tick * hours) self.hands[1].transform = ui.Transform.rotation(tick * minutes) self.hands[2].transform = ui.Transform.rotation(tick * seconds) v = ui.View(frame=(0,0,600,600)) v1 = AnalogClock(frame=(0,0,200,200)) v2 = AnalogClock(frame=(0,300,200,200)) v3 = AnalogClock(frame=(300,0,200,200)) v4 = AnalogClock(frame=(300,300,200,200)) v.add_subview(v1) v.add_subview(v2) v.add_subview(v3) v.add_subview(v4) v.present('sheet')
With timezone code
https://gist.github.com/19e99d748397855003afdebf32dafc3a -
@enceladus , thanks. There appears to be a small draw problem on the clock face being squared off. I didn't try to fix it, but will look,at it. Below is your code with a minor adjustment to add time zones. There is no error checking around the tz_str passed, it has to be a valid tz string from pytz. The reason, I did not do more with this is because the strings not very nice. A better approach would be to use the arrow lib, I will do that later. Thought it was better to stick the included Libs to begin with.
But thanks again. I will spend more time looking at the code soon as I have time. I just want to add the start of time zones.import ui from math import pi, sin, cos from datetime import datetime import pytz LabelNode = ui.Label SpriteNode = ui.ImageView class ShapeNode(ui.View): def __init__(self, path=None, fill_color='white', stroke_color='clear', shadow=None, *args, **kwargs): self.path = path self.fill_color = fill_color self.stroke_color = stroke_color super().__init__(*args, **kwargs) def draw(self): ui.set_color(self.fill_color) self.path.fill() ui.set_color(self.stroke_color) self.path.stroke() class AnalogClock(ui.View): def __init__(self, tz_str = None, *args, **kwargs): super().__init__(*args, **kwargs) self.tz_str = tz_str center_x, center_y = self.center self.w1 = min(self.height, self.width) center_x, center_y = (self.w1/2, self.w1/2) r = (self.w1/2) * 0.9 #print(r) circle = ui.Path.oval(0, 0, r*2, r*2) circle.line_width = 6 shadow = ('black', 0, 0, 15) frame = (center_x -r, center_y - r, 2*r, 2*r) self.face = ShapeNode(circle, 'white', 'silver', shadow=shadow, frame=frame ) self.add_subview(self.face) for i in range(12): a = 2 * pi * (i+1)/12.0 -pi/2 label = LabelNode(text='{:2d}'.format(i+1), font=('HelveticaNeue-UltraLight', 0.2*r), text_color='black', frame=(cos(a)*(r*0.85)+center_x-.1*r, sin(a)*(r*0.85)+center_y-r*.85, 2*r*.85, 2*r*.85)) self.add_subview(label) self.hands = [] self.update_interval = .1 hand_attrs = [(r*0.6, 8, 'black'), (r*0.9, 8, 'black'), (r*0.9, 4, 'red')] for l, w, color in hand_attrs: shape = ShapeNode(ui.Path.rounded_rect(l-w/2, 0, w, l, w/2), color, frame=(center_x-l, center_y-l, 2*l, 2*l)) #shape.anchor_point = (0.5, 0) self.hands.append(shape) self.add_subview(shape) self.add_subview(ShapeNode(ui.Path.oval(0, 0, 15, 15), 'black', frame=(center_x-7.5, center_y-7.5, 15, 15))) def get_dt(self): # return a datetime object for the current datetime for self.tz_str time zone, otherwise # the hardwares datetime setting return datetime.now(pytz.timezone(self.tz_str)) if self.tz_str else datetime.now() def update(self): #t = datetime.now() t = self.get_dt() tick = 2 * pi / 60.0 seconds = t.second + t.microsecond/1000000.0 minutes = t.minute + seconds/60.0 hours = (t.hour % 12) + minutes/60.0 self.hands[0].transform = ui.Transform.rotation(5 * tick * hours) self.hands[1].transform = ui.Transform.rotation(tick * minutes) self.hands[2].transform = ui.Transform.rotation(tick * seconds) v = ui.View(frame=(0,0,600,600)) v1 = AnalogClock('US/Pacific', frame=(0,0,200,200)) v2 = AnalogClock('Asia/Bangkok', frame=(0,300,200,200)) v3 = AnalogClock('Singapore', frame=(300,0,200,200)) v4 = AnalogClock(frame=(300,300,200,200)) # should be the devices local time v.add_subview(v1) v.add_subview(v2) v.add_subview(v3) v.add_subview(v4) v.present('sheet')
-
The gist below my post contains the code with timezone. Anyway it is almost like what you have.
-
@enceladus , sorry I missed the gist reference on your post. But yes almost identical approach for tz. Not sure if you have used arrow or not. But it would make the time zones a lot nicer. I have arrow installed, but will wait to later to use it. I will just do a little to adding a label into the clock face for the time zone and or a label added the bottom of the clock's bounding rect.
-
Oh, I should clear up a mistake/assumption I made. It appears that at least in the latest beta, the documentation states under undocumented modules that arrow ships with Pythonista.
That's nice.