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.DatePicker countdown display D,H,M,S
-
Hey guys,
I was playing around with date picker so I could create a quick program to calculate the time until a future date for my 7 year old daughter. She always asks us “how long until my birthday??” So I made her a program that will tell her the Days, Hours, Minutes, Seconds until the date she selected in the ui.DatePicker.
Currently, she selects a date and it outputs the delta into a textView(4 of them, one for each: D,H,M,S), which get updated in real-time (Spams the terminal if debug flag is true, but I’m cool with that).
What I would like to do is replace the textViews with another DatePicker.
Unfortunately, none of the modes available display all fields: D,H,M,S.Is there a way for me to do this?
Ideally, I’d like a copy of the source code for DatePicker so I could resize the width of the segments to be smaller(so it all fits) and then create additional segments to display D,H,M,S and rotate each dial in real-time.Here is my working code as of right now:
import ui import console import time import datetime from time import * from objc_util import * from datetime import timedelta#, datetime def run_async(func): from threading import Thread from functools import wraps @wraps(func) def async_func(*args, **kwargs): func_hl = Thread(target = func, args = args, kwargs = kwargs) func_hl.start() return func_hl return async_func birthday = [2021, 2, 21] # TODO: ##### MAIN VIEW ##### birthdayView = ui.View() birthdayView.background_color = 'black' birthdayView.x = 0 birthdayView.y = 0 birthdayView.width = 100 birthdayView.height = 100 birthdayView.flex = "LRTB" ######################### # TODO: ##### DATE PICKER ##### dateSelector = ui.DatePicker() dateSelector.center = (birthdayView.width / 2, birthdayView.height / 2) dateSelector.x = birthdayView.center[0] dateSelector.y = birthdayView.center[1] dateSelector.background_color = "#AAA" dateSelector.mode = 1 ######################### # TODO: ##### DAYS OUTPUT FIELD ##### daysOutputField = ui.TextView() daysOutputField.x = birthdayView.center[0] - 15 daysOutputField.y = birthdayView.center[1] + 300 daysOutputField.width = 350 daysOutputField.height = 50 daysOutputField.background_color = "#000" daysOutputField.text_color = "#F00" daysOutputField.font = "<system>", 18 ######################### # TODO: ##### HOURS OUTPUT FIELD ##### hoursOutputField = ui.TextView() hoursOutputField.x = birthdayView.center[0] - 15 hoursOutputField.y = birthdayView.center[1] + 355 hoursOutputField.width = 350 hoursOutputField.height = 50 hoursOutputField.background_color = "#000" hoursOutputField.text_color = "#F00" hoursOutputField.font = "<system>", 22 ######################### # TODO: ##### MINUTES OUTPUT FIELD ##### minutesOutputField = ui.TextView() minutesOutputField.x = birthdayView.center[0] - 15 minutesOutputField.y = birthdayView.center[1] + 410 minutesOutputField.width = 350 minutesOutputField.height = 50 minutesOutputField.background_color = "#000" minutesOutputField.text_color = "#F00" minutesOutputField.font = "<system>", 26 ######################### # TODO: ##### SECONDS OUTPUT FIELD ##### secondsOutputField = ui.TextView() secondsOutputField.x = birthdayView.center[0] - 15 secondsOutputField.y = birthdayView.center[1] + 465 secondsOutputField.width = 350 secondsOutputField.height = 50 secondsOutputField.background_color = "#000" secondsOutputField.text_color = "#F00" secondsOutputField.font = "<system>", 30 ######################### #import datetime #from time import localtime, time, gmtime, mktime, struct_time # TODO: ##### DEBUG FLAG ##### debugMode = 1 ######################### @run_async def calculateTimeUntilBirthday(sender): global birthday global dateSelector global countdownTimer global daysOutputField global hoursOutputField global minutesOutputField global secondsOutputField while birthdayView.on_screen: currentTime = datetime.datetime.now() birthdayTime = dateSelector.date.replace(hour = 0, minute = 0, second = 0) birthdayString = f"{birthdayTime.month}/{birthdayTime.day}/{birthdayTime.year}" timeUntilBirthday = (birthdayTime) - (currentTime) seconds = timeUntilBirthday.seconds hours = seconds / 3600 minutesLeft = hours - int(hours) minutesLeftMinutes = minutesLeft * 60 secondsLeft = minutesLeftMinutes - int(minutesLeftMinutes) secondsLeftSeconds = secondsLeft * 60 secondsLeftSeconds = round(secondsLeftSeconds, 2) daysOutput = f"Days: {timeUntilBirthday.days}" daysOutputField.text = daysOutput hoursOutput = f"Hours: {int(hours)}" hoursOutputField.text = hoursOutput minutesOutput = f"Minutes: {int(minutesLeftMinutes)}" minutesOutputField.text = minutesOutput secondsOutput = f"Seconds: {int(secondsLeftSeconds)}" secondsOutputField.text = secondsOutput if debugMode: print(f"Current Time: {currentTime}") print(f"Birthday Time: {birthdayTime}") print(f"birthdayString: {birthdayString}") print(f"timeUntilBirthday: {timeUntilBirthday}") print(f"seconds: {seconds}") print(f"hours: {hours}") print(f"minutesLeft: {minutesLeft}") print(f"minutesLeftMinutes: {minutesLeftMinutes}") print(f"secondsLeft: {secondsLeft}") print(f"secondsLeftSeconds: {secondsLeftSeconds}") print(f"secondsLeftSeconds: {secondsLeftSeconds}") print(f"Time Until Birthday: {birthdayString}\n Days: {timeUntilBirthday.days}\n Hours: {int(hours)}\n Minutes: {int(minutesLeftMinutes)}\n Seconds: {int(secondsLeftSeconds)}") #birthdayView.add_subview(countdownTimer) birthdayView.add_subview(dateSelector) birthdayView.add_subview(daysOutputField) birthdayView.add_subview(hoursOutputField) birthdayView.add_subview(minutesOutputField) birthdayView.add_subview(secondsOutputField) dateSelector.action = calculateTimeUntilBirthday birthdayView.present(hide_title_bar = True)
-
@Robert_Tompkins try this
# coding: utf-8 from math import pi,cos,sin from objc_util import * import ui #===================== delegate of UIPickerView: begin ===================== def pickerView_numberOfRowsInComponent_(self, cmd, picker_view, component): return(len(ObjCInstance(picker_view).data)) def numberOfComponentsInPickerView_(self, cmd, picker_view): return 1 def rowSize_forComponent_(self, cmd, picker_view, component): return ObjCInstance(picker_view).myRowWidth def pickerView_rowHeightForComponent_(self, cmd, picker_view, component): return ObjCInstance(picker_view).myRowHeight def pickerView_didSelectRow_inComponent_(self, cmd, picker_view, row, component): UIPickerView = ObjCInstance(picker_view) UIPickerView.selectedRow = row return def pickerView_viewForRow_forComponent_reusingView_(self, cmd, picker_view, row, component,view_ptr): UIPickerView = ObjCInstance(picker_view) if view_ptr == None: #view = ObjCClass('UILabel').alloc().init() #view.setText_(UIPickerView.data[row]) uiview = ui.Label() uiview.alignment = ui.ALIGN_CENTER uiview.text_color = 'red' uiview.text = UIPickerView.data[row] view = ObjCInstance(uiview) view.setClipsToBounds_(True) if UIPickerView.horiz: a = pi/2 rot = CGAffineTransform(cos(a),-sin(a),sin(a),cos(a),0,0) view.setTransform_(rot, restype=None, argtypes=[CGAffineTransform]) else: view = ObjCInstance(view_ptr) return view.ptr methods = [ numberOfComponentsInPickerView_, pickerView_numberOfRowsInComponent_, rowSize_forComponent_, pickerView_rowHeightForComponent_, pickerView_didSelectRow_inComponent_, pickerView_viewForRow_forComponent_reusingView_] protocols = ['UIPickerViewDataSource', 'UIPickerViewDelegate'] UIPickerViewDataSourceAndDelegate = create_objc_class( 'UIPickerViewDataSourceAndDelegate', methods=methods, protocols=protocols) #===================== delegate of UIPickerView: end ======================= class MyUIPickerView(ui.View): def __init__(self, data, horiz=False,**kwargs): super().__init__(**kwargs) UIPickerView = ObjCClass('UIPickerView') self._picker_view = UIPickerView.alloc().initWithFrame_(ObjCInstance(self).bounds()).autorelease() ObjCInstance(self).addSubview_(self._picker_view) self.delegate_and_datasource = UIPickerViewDataSourceAndDelegate.alloc().init().autorelease() self._picker_view.delegate = self.delegate_and_datasource self._picker_view.dataSource = self.delegate_and_datasource self._picker_view.myRowWidth = self.width self._picker_view.myRowHeight = 32 self._picker_view.data = data self._picker_view.horiz = horiz if horiz: a = -pi/2 rot = CGAffineTransform(cos(a),-sin(a),sin(a),cos(a),0,0) self._picker_view.setTransform_(rot, restype=None, argtypes=[CGAffineTransform]) def layout(self): self._picker_view.frame = ObjCInstance(self).bounds() def main(): mv = ui.View() mv.frame = (0,0,500,500) mv.background_color = 'white' days = [] for i in range(0,100): days.append(str(i)) pvd = MyUIPickerView(days, frame=(50,50,80,200)) mv.add_subview(pvd) hours = [] for i in range(0,24): hours.append(str(i)) pvh = MyUIPickerView(hours,frame=(150,50,80,200)) mv.add_subview(pvh) mins = [] for i in range(0,60): mins.append(str(i)) pvm = MyUIPickerView(mins, frame=(250,50,80,200)) mv.add_subview(pvm) secs = [] for i in range(0,60): secs.append(str(i)) pvs = MyUIPickerView(secs, frame=(350,50,80,200)) mv.add_subview(pvs) mv.present('sheet') if __name__ == '__main__': main()
-
Sweet, looks like all the bits n pieces are there!
I haven’t played around with objc_util much, so it will take some trial and error to get what I want.However, I see that module used all over the place, and likely for good reason.
So I will play with it and once I get a feel for it, will see where else I can use it.Thanks!
-
@Robert_Tompkins if you need some help, don't hesitate to ask
-
@cvp
I need some help ;)Been struggling because I have not used objc previously. So syntax and how it’s being converted on the fly to be used to access objects, etc.. is brand new to me.
I have been trying to play with it via adjusting my screen brightness using one of the built in objc examples:
from objc_util import * from time import sleep def run_async(func): from threading import Thread from functools import wraps @wraps(func) def async_func(*args, **kwargs): func_hl = Thread(target = func, args = args, kwargs = kwargs) func_hl.start() return func_hl return async_func @on_main_thread def setScreenBrightness(): UIScreen = ObjCClass('UIScreen') screen = UIScreen.mainScreen() #print(f"UIScreen.get_names(): {UIScreen.get_names()}") for x in range(1): brightnessVar = 1.0 for x in range(995): screen.setBrightness(brightnessVar) brightnessVar -= 0.001 sleep(0.0001) #print(f"screen.brightness(): {screen.brightness()}") for x in range(995): if screen.brightness() < 1.0: screen.setBrightness(brightnessVar) brightnessVar += 0.001 sleep(0.0001) setScreenBrightness()
However, what would help is being able to see a list of ‘objects’ I can get/set attributes for.
For example:UIScreen = ObjCClass('UIScreen') screen = UIScreen.mainScreen()
What is UIScreen? I assume we are letting objc create a class/do conversions/do its magic to allow us to access/modify properties of it. But what other strings/object classes are available? Is there something like ‘UIFlashlight’?
Of course, my attempts to print out a list of ‘things’ to try and understand structure, etc.. have resulted in a HUGE list being returned.. But for me to be able to learn this stuff, I get the most out of playing around with things, tweaking, printing out what’s there, but I can’t seem to do this. But again, I don’t even know where to begin haha.
Any resources, tips you have send them my way! But as far as the countdown code you gave me; that will work perfectly. Once I understand the basics I will be rolling through the rest.
-
Searching apple docs is useful, as it let's you know what is in each class.
https://github.com/jsbain/objc_hacks/blob/master/objc_browser.py
Can be useful for exploring undocumented classes -- it lists all classes available on your device at the moment (might not include frameworks that haven't been loaded yet), then let's you see the method names. (And then let's you highjack the method invocations to log the input types,for methods called elsewhere)When reading apple api docs, be sure to set the little switch to look at objc, not swift. Then you can usually figure out the python version of the name by replacing the colons with underscores.
ObjCClass (classname) returns a class object,not an instance. Usually you will want to either use .new() to get an instance, or .alloc().init(), or maybe .alloc().initwithSomeParam_() if the constructor takes arguments. Calling obj=ObjCClass(classname).alloc(), then using autocomplete in the console by typing obj, then a period,then start typing,you can see what options you have. Some objects have a shared singleton that you use-- for instance UIApplication.sharedApplication(). Browsing using autocomplete in the console is useful for discovering that sort of thing.
-
@JonB
Sweet, I actually have been browsing the Apple developer docs, specially UIKit stuff, because that is what I keep finding examples of and I believe that’s what OMZ based the UI elements on.I managed to find some stuff there, as well as created a method to help investigate objects, etc.
def investigateItem(item): mysteriousItem = item mysteriousInfo = dir(mysteriousItem) for x in mysteriousInfo: print(f"{x}") #setScreenBrightness() UIScreen = ObjCClass('UIScreen') screen = UIScreen.mainScreen() investigateItem(screen)
However, I totally forgot about using the console... That is a game changer, thanks!
I’ll check out the other info you mentioned after I play a bit via console. Is it weird that I’m excited right now? Haha. Using the console to interact/access/view attributes, etc. is going to be crazy useful. -
@Robert_Tompkins Usually, I only do "print(dir(screen))"..
-
@Robert_Tompkins obviously too late because here we just wake up.
screen.setBrightness_(brightnessVar)
See the underscore