Today Widget - Just some comments
Sorry if this has been covered before.
But I tried to use the Today Widget, today (I have tried before, but long time ago). Anyway, I was suprised about what I could do.
I was able to load a CustomView using @JonB PYUILoader (loading a pyui file into a Custom Class) , I was able to use the update method in the custom class to update a timer value. Interact with a database (a single call for now).
Anyway, it all just works. Well execpt for the bg_color. The way I am doing it, I end up with a white bg_color. I tried setting the bg_color = None, that does not work. I will have to find out how to do that.
but the below code is not runable on anothers device, I just wanted to start something and see if things like update method would work.
I am not sure if different devices get more memory to run today widgets or not.
So the below code is not meant to be smart/polished. Just wanted to see if it would work.
#!python3 import appex import ui import arrow from tinydb import TinyDB, where class PYUILoader(ui.View): # this acts as a normal Custom ui.View class # the root view of the class is the pyui file read in def WrapInstance(obj): class Wrapper(obj.__class__): def __new__(cls): return obj return Wrapper def __init__(self, pyui_fn, *args, **kwargs): bindings = globals().copy() bindings[self.__class__.__name__] = self.WrapInstance() ui.load_view(pyui_fn, bindings) # call after so our kwargs modify attrs super().__init__(*args, **kwargs) def GetTimerFinished(db_fn, timer_name): with TinyDB(db_fn) as db: rec = db.search(where('timer_name') == timer_name) return rec def SetTimerFinished(db_fn, timer_name, days=0, hours=0, minutes=0): utc = arrow.utcnow() finish_time = utc.shift(days=+days, hours=+hours, minutes=+minutes) rec = dict(timer_name=timer_name, entered=utc.for_json(), finish_time=finish_time.for_json(), elapased=False, ) with TinyDB(db_fn) as db: db.upsert(rec, where('timer_name') == rec['timer_name']) return rec class AppexTestClass(PYUILoader): def __init__(self, pyui_fn, db_fn, timer_name, *args, **kwargs): super().__init__(pyui_fn, *args, **kwargs) #self.bg_color = None self.db_fn = db_fn self.timer_name = timer_name self.local_finish_time = 0 rec = GetTimerFinished(db_fn, self.timer_name) self.update_interval = 1 self.count = 0 self.expired = False self.calc_time() self['f_timer_name'].text = rec['timer_name'] def calc_time(self): timer_rec = GetTimerFinished(self.db_fn, self.timer_name) self.timer_name = timer_rec['timer_name'] utc = arrow.get(timer_rec['finish_time']) local_finish_time = utc.to('Asia/Bangkok') self.local_finish_time = local_finish_time if self.local_finish_time < arrow.now(): self.expired = True def time_remaining(self): return self.local_finish_time - arrow.now() def update(self): if self.expired: self['f_time_left'].text = 'Expired' return self['f_time_left'].text =\ self.format_timedelta(self.local_finish_time - arrow.now()) def format_timedelta(self, td): hours, remainder = divmod(td.total_seconds(), 3600) minutes, seconds = divmod(remainder, 60) hours, minutes, seconds = int(hours), int(minutes), int(seconds) if hours < 10: hours = '0%s' % int(hours) if minutes < 10: minutes = '0%s' % minutes if seconds < 10: seconds = '0%s' % seconds return '%s:%s:%s' % (hours, minutes, seconds) def main(): db_fn = 'my_timers.json' pyui_fn = 'appex_test_ui.pyui' timer_name = 'Wonder Wars' #SetTimerFinished(db_fn, timer_name, hours=5, minutes=35 ) v = AppexTestClass(pyui_fn, db_fn, timer_name) appex.set_widget_view(v) if __name__ == '__main__': main()
def format_timedelta(self, td): hours, remainder = divmod(td.total_seconds(), 3600) minutes, seconds = divmod(remainder, 60) return '%02.0f:%02.0f:%02.0f' % (hours, minutes, seconds)
@ccc , thanks. Actually I just copied that code from Stackflow. I hadn't realised before that there is no built formatting for TimeDelta objects. The method posted is also good enough as its limited to by hours. I got another code snippet off ActiveCode that handles Weeks all the way down to microsecond's if you need it. The code is here.
I am still looking at the arrow docs to see if it has support to format TimeDelta objects. As far as I see it doesn't. When you do a calculation operation in arrow, you are returned a TimeDelta object, not an arrow object. Just for fun, I will look at Maya to see if it supports it. Would not make me swap if it did though because arrow ships with Pythonista. Oh, arrow has the humanise method, not the same but still handy.
I went through the code above more closely and see I had quite a few mistakes I fixed up. Mainly the way I built it up. Wasn't sure what would work. I am suprised it works. Using the PYUILoader and update functions etc... But I guess its a nice testament to how well things are put together in Pythonista.
The other thing that I was able to do was add a table to the view. I didn't try interacting with it. Just added a TableView on in the Designer. I think this maybe getting to the edge though, as I often got 'Unable to Load' message in the Today view. I didn't realise at first, but that message is a button. If you tap it the view loads.
Screen Shot of TableView in Today widget. Maybe this is no news for a lot of people. I am not sure I have seen a scrolling table in a Today Widget before (I mean from an company).
Regarding the clear background thing: I’ve just started playing with the Today Widget as well and I used this code to load a standard PYUI file (as might be done for a normal script). It also sets a clear background. I hope it’s helpful!
v = ui.load_view() v.background_color = ui.set_color("clear") appex.set_widget_view(v)
@chriswilson , thanks for the reply. However, I am not sure "clear" is actually recognised as a legitimate param (if its a css name then it would be), but calling ui.set_color(x) if x is not recognised it will set the bg_color to (0.0, 0.0, 0.0, 0.0) regardless - tuple of RGBA. Eg. You could call ui.set_color(None) or ui.set_color("Hey_Jude") and it will still return (0.0, 0.0, 0.0, 0.0).
Again, maybe "clear" is recognised, but maybe its not, and the default behaviour for an un-recognised param is being returned
Ah I see. Good to know. I tried this and it got me a nice transparent background to my widget, but of course setting alpha to zero would do the same thing!
I was basing this on the Swift iOS colours (UIColor class which has a UIColor.clear) rather than CSS. I’m not sure if this is what is going on behind the scenes of the UI module though.
@chriswilson, I just looked up the css colours and there is no 'clear' defined. It makes sense as its only RGB. But it still could have been an alias representation, you never know :)
But as you say its still good to find out.