├── .github └── FUNDING.yml ├── .gitignore ├── warp_speed.py ├── OfflineWebView.py ├── TopTenView.py ├── save_py_source.py ├── segmented_table.py ├── temperature_gui.py ├── ValidatingView.py ├── button_copy.pyui ├── CharCountView.py ├── GetSomeTextView.py ├── ui_textview_save_on_close.py ├── ButtonView.py ├── TimedRefreshView.py ├── ScreenshotView.py ├── button_copy.py ├── colors_of_the_week.py ├── ScreenshotWebView.py ├── morse_code.py ├── MorseView.py ├── pyui_embed.py ├── ThreeBackticks.py ├── burgers_and_beers.py ├── BikeStationsView.py ├── AnimalMatchView.py ├── TicTacToe.py └── LICENSE /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [cclauss] 2 | patreon: cclauss 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # Installer logs 26 | pip-log.txt 27 | pip-delete-this-directory.txt 28 | 29 | # Unit test / coverage reports 30 | htmlcov/ 31 | .tox/ 32 | .coverage 33 | .cache 34 | nosetests.xml 35 | coverage.xml 36 | 37 | # Translations 38 | *.mo 39 | 40 | # Mr Developer 41 | .mr.developer.cfg 42 | .project 43 | .pydevproject 44 | 45 | # Rope 46 | .ropeproject 47 | 48 | # Django stuff: 49 | *.log 50 | *.pot 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | -------------------------------------------------------------------------------- /warp_speed.py: -------------------------------------------------------------------------------- 1 | #http://trekcore.com/audio/warp/tng_warp4_clean.mp3 2 | #http://trekcore.com/audio/warp/tng_warp_out4.mp3 3 | 4 | import requests, sound, ui 5 | #download sound effects from the web 6 | url_fmt = 'http://trekcore.com/audio/warp/{}.mp3' 7 | filenames = 'tng_warp_out4.caf tng_warp4_clean.caf'.split() 8 | for filename in filenames: 9 | with open(filename, 'wb') as out_file: 10 | url = url_fmt.format(filename.rstrip('.caf')) 11 | out_file.write(requests.get(url).content) 12 | sound.load_effect(filename) 13 | 14 | def warp_action(sender): 15 | sound.play_effect(filenames[sender.value]) 16 | 17 | sw = ui.Switch() 18 | sw.action = warp_action 19 | view = ui.View(name = 'Captain Picard Wants Warp Speed') 20 | view.add_subview(sw) 21 | view.present() 22 | sw.center = view.center 23 | -------------------------------------------------------------------------------- /OfflineWebView.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # See: https://forum.omz-software.com/topic/2450/save-webpage-for-offline 4 | 5 | import requests, ui 6 | 7 | url = 'https://en.m.wikipedia.org/wiki/Python_(programming_language)' 8 | filename = (url.rpartition('/')[2] or url) + '.html' 9 | 10 | try: 11 | resp = requests.get(url) 12 | encoding = resp.encoding # UTF-8 13 | html = resp.text 14 | except requests.ConnectionError: 15 | html = '' 16 | 17 | if html: 18 | with open(filename, 'w', encoding=encoding) as out_file: 19 | out_file.write(html) 20 | else: 21 | try: 22 | with open(filename) as in_file: 23 | html = in_file.read() 24 | except IOError: # file not found 25 | html = 'No connection to Wikipedia and article not found in local cache.' 26 | 27 | web_view = ui.WebView() 28 | web_view.load_html(html) 29 | web_view.present() 30 | -------------------------------------------------------------------------------- /TopTenView.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # ui.View subclass for the top ten iTunes songs. 4 | # Pull requests gladly accepted. 5 | 6 | import feedparser, requests, ui 7 | 8 | url = 'https://itunes.apple.com/us/rss/topsongs/limit=10/xml' 9 | 10 | def get_image_urls(itunes_url): 11 | for entry in feedparser.parse(itunes_url).entries: 12 | yield entry['summary'].partition('src="')[2].partition('"')[0] 13 | 14 | class TopTenView(ui.View): 15 | def __init__(self, image_urls): 16 | self.present() 17 | for i, url in enumerate(image_urls): 18 | button = ui.Button() 19 | button.background_image = ui.Image.from_data(requests.get(url).content) 20 | w, h = button.background_image.size 21 | button.x = i % 5 * w 22 | button.y = i // 5 * h 23 | button.width, button.height = w, h 24 | button.border_width = 2 25 | self.add_subview(button) 26 | 27 | TopTenView(list(get_image_urls(url))) 28 | -------------------------------------------------------------------------------- /save_py_source.py: -------------------------------------------------------------------------------- 1 | import datetime, os, zipfile 2 | 3 | exts = 'py pyui'.split() 4 | zip_file_name = 'aa_source_code_%Y_%m_%d_%H_%M_%S.zip' 5 | zip_file_name = datetime.datetime.strftime(datetime.datetime.now(), zip_file_name) 6 | 7 | def get_filenames(in_dir=None): 8 | def visit(_, dirname, names): 9 | for name in names: 10 | filename = os.path.join(dirname, name) 11 | if os.path.isfile(filename): 12 | filenames.append(filename) 13 | 14 | filenames = [] 15 | os.path.walk(in_dir or os.curdir, visit, None) 16 | return filenames 17 | 18 | filenames = get_filenames() 19 | if exts: 20 | filenames = [fn for fn in filenames if os.path.splitext(fn)[1] in exts] 21 | file_count = len(filenames) 22 | print('{} files found.'.format(file_count)) 23 | if filenames: 24 | with zipfile.ZipFile(zip_file_name, 'w') as zip_file: 25 | for i, filename in enumerate(filenames): 26 | zip_file.write(filename) 27 | if not i % 50: 28 | print('{} of {}: {}'.format(i, file_count, filename)) 29 | print('{}\n{} files copied into zip file: "{}".'.format('=' * 13, file_count, zip_file_name)) 30 | -------------------------------------------------------------------------------- /segmented_table.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # See: https://forum.omz-software.com/topic/2374/is-there-a-way-to-programmatically-highlight-a-ui-segmentedcontrol 4 | 5 | import ui 6 | 7 | class SegNav(ui.View): 8 | def __init__(self): 9 | self.present() 10 | self.name = 'SegNav' 11 | seg = ui.SegmentedControl() 12 | seg.action = self.seg_view_action 13 | seg.background_color = 'white' 14 | seg.flex = 'W' 15 | seg.height = 40 16 | seg.segments = 'even', 'odd' 17 | seg.selected_index = 0 # set the highlight 18 | seg.width = self.bounds.w 19 | self.add_subview(seg) 20 | 21 | x, y, w, h = self.bounds 22 | self.table_view = ui.TableView() 23 | self.table_view.data_source = ui.ListDataSource(range(0, 42, 2)) 24 | self.table_view.flex = 'WH' 25 | self.table_view.frame = x, y + seg.height, w, h - seg.height 26 | self.add_subview(self.table_view) 27 | 28 | def seg_view_action(self, sender): 29 | # print(sender.segments[sender.selected_index]) 30 | self.table_view.data_source.items = range(sender.selected_index, 42, 2) 31 | 32 | SegNav() 33 | -------------------------------------------------------------------------------- /temperature_gui.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # http://omz-forums.appspot.com/pythonista/post/5809271396630528 4 | # calculations are NOT correct!!! 5 | 6 | import ui 7 | 8 | fmt = '''{:g} degrees Fahrenheit is {:g} degrees Celsius. 9 | 10 | {:g} degrees Celsius is {:g} degrees Fahrenheit.''' 11 | 12 | def convert_action(sender): 13 | try: 14 | value = float(text_view.text.strip()) 15 | except ValueError: 16 | value = 50 17 | text_view.text = '{:g}'.format(value) 18 | label.text = fmt.format(value, value * 1.2, value, value * 0.8) 19 | 20 | view = ui.View(name='Temperature Converter') 21 | view.hidden = True 22 | view.present() 23 | _, _, w, h = view.bounds 24 | 25 | text_view = ui.TextView() 26 | text_view.alignment = ui.ALIGN_CENTER 27 | text_view.center = (w/2, h/4) 28 | 29 | button = ui.Button(title='Convert') 30 | button.action = convert_action 31 | button.center = (w/2, h/2) 32 | 33 | label = ui.Label() 34 | label.width = w/2 35 | label.alignment = ui.ALIGN_CENTER 36 | label.background_color = 'white' 37 | label.center = (w/2, h*3/4) 38 | label.number_of_lines = 4 39 | label.text = 'Enter a temperature above and tap "Convert".' 40 | 41 | view.add_subview(text_view) 42 | view.add_subview(button) 43 | view.add_subview(label) 44 | view.hidden = False 45 | -------------------------------------------------------------------------------- /ValidatingView.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import ui 4 | 5 | # See: https://forum.omz-software.com/topic/2499/textfield-validation-example 6 | 7 | class ValidatingView(ui.View): 8 | def __init__(self): 9 | for name in 'lower upper title numeric'.split(): 10 | text_field = ui.TextField(name=name) 11 | text_field.delegate = self 12 | text_field.height = 24 13 | text_field.text = name if name != 'numeric' else 'a1b2c3d4e5f' 14 | self.add_subview(text_field) 15 | self.textfield_did_change(text_field) 16 | self.present() 17 | 18 | def layout(self): 19 | for i, subview in enumerate(self.subviews): 20 | subview.width = self.width 21 | subview.y = (i + 1) * (subview.height + 10) 22 | 23 | def textfield_did_change(self, textfield): 24 | if textfield.name == 'lower': 25 | textfield.text = textfield.text.lower() 26 | elif textfield.name == 'upper': 27 | textfield.text = textfield.text.upper() 28 | elif textfield.name == 'title': 29 | textfield.text = textfield.text.title() 30 | elif textfield.name == 'numeric': 31 | textfield.text = ''.join(c for c in textfield.text if c.isdigit()) 32 | 33 | ValidatingView() 34 | -------------------------------------------------------------------------------- /button_copy.pyui: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "class" : "View", 4 | "attributes" : { 5 | "custom_class" : "", 6 | "background_color" : "RGBA(1.000000,1.000000,1.000000,1.000000)", 7 | "tint_color" : "RGBA(0.000000,0.478000,1.000000,1.000000)", 8 | "enabled" : true, 9 | "border_color" : "RGBA(0.000000,0.000000,0.000000,1.000000)", 10 | "flex" : "" 11 | }, 12 | "frame" : "{{0, 0}, {939, 534}}", 13 | "nodes" : [ 14 | { 15 | "class" : "Button", 16 | "attributes" : { 17 | "background_color" : "RGBA(0.000000,0.666667,1.000000,1.000000)", 18 | "border_color" : "RGBA(0.000000,0.000000,1.000000,1.000000)", 19 | "font_size" : 15, 20 | "title" : "Blue", 21 | "enabled" : true, 22 | "tint_color" : "RGBA(0.000000,0.000000,1.000000,1.000000)", 23 | "flex" : "", 24 | "action" : "button_action", 25 | "font_bold" : true, 26 | "alpha" : 0.5000000000000001, 27 | "name" : "Blue", 28 | "border_width" : 2, 29 | "uuid" : "0F0AEC82-2A01-4D53-A7C2-75C5EF40C67E", 30 | "corner_radius" : 4 31 | }, 32 | "frame" : "{{50, 50}, {130, 35.5}}", 33 | "nodes" : [ 34 | 35 | ] 36 | } 37 | ] 38 | } 39 | ] 40 | -------------------------------------------------------------------------------- /CharCountView.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import string, ui 4 | 5 | class CharCountView(ui.View): 6 | def __init__(self): 7 | self.background_color = 'white' 8 | self.name = 'Char count: Unicode chars are multiple bytes long' 9 | self.add_subview(ui.Label(name='chars')) 10 | self.add_subview(ui.Label(name='bytes')) 11 | for sv in self.subviews: 12 | sv.alignment = ui.ALIGN_CENTER 13 | sv.font = '', 14 14 | text_view = ui.TextView(name='text') 15 | text_view.delegate = self 16 | text_view.font = '', 24 17 | text_view.text = __file__.rpartition('/')[2][:-3] + ' Ü' 18 | self.add_subview(text_view) 19 | self.textview_did_change(text_view) 20 | self.present() 21 | 22 | def layout(self): 23 | x, y, w, h = self.bounds 24 | label_height = 20 25 | self['chars'].frame = x, y, w / 2, label_height 26 | self['bytes'].frame = x + w / 2, y, w / 2, label_height 27 | self['text'].frame = x, y + label_height, w, h - label_height 28 | 29 | def textview_did_change(self, textview): 30 | self['chars'].text = '{} chars'.format(len(textview.text)) 31 | self['bytes'].text = '{} bytes'.format(len(bytes(textview.text))) 32 | 33 | CharCountView() 34 | -------------------------------------------------------------------------------- /GetSomeTextView.py: -------------------------------------------------------------------------------- 1 | # See: http://omz-forums.appspot.com/pythonista/post/5282094997569536 2 | 3 | import ui 4 | 5 | class GetSomeTextView(ui.View): 6 | def __init__(self): 7 | self.name = 'Enter some text:' 8 | tf1 = self.make_text_field('top_field', 'Change me') 9 | tf2 = self.make_text_field('bottom_field', 'Me too') 10 | tf1.action = tf2.action = self.textfield_action 11 | tf1.y += 20 ; tf2.y += 60 # move them down 12 | self.width = self.height = 300 13 | self.present('sheet') 14 | print('-' * 20) 15 | self.wait_modal() 16 | 17 | def make_text_field(self, name, default_text = ''): 18 | text_field = ui.TextField(name=name) 19 | text_field.text = default_text 20 | text_field.height = 20 21 | text_field.width = 200 22 | text_field.flex = 'LR' # centered in its superview 23 | self.add_subview(text_field) 24 | return text_field 25 | 26 | def textfield_action(self, sender): 27 | fmt = 'Field {:<12} has a action value of "{}".' 28 | print(fmt.format(sender.name, sender.text)) 29 | 30 | def will_close(self): 31 | fmt = 'Field {:<12} has a final value of "{}".' 32 | for subview in self.subviews: 33 | if isinstance(subview, ui.TextField): 34 | print(fmt.format(subview.name, subview.text)) 35 | 36 | GetSomeTextView() 37 | -------------------------------------------------------------------------------- /ui_textview_save_on_close.py: -------------------------------------------------------------------------------- 1 | import ui 2 | 3 | filename = 'name.txt' 4 | 5 | class GetUsernameView(ui.View): 6 | def __init__(self, username=None): 7 | self.name = self.__class__.__name__ 8 | textfield = ui.TextView(name='namefield') 9 | textfield.height = 25 10 | textfield.text = self.username = username or self.read_username() 11 | self.add_subview(textfield) 12 | 13 | self.hidden = True 14 | self.width = self.height = min(ui.get_screen_size()) 15 | self.present('sheet') 16 | # somtimes it is good to be self-centered 17 | textfield.center = self.center 18 | self.hidden = False 19 | 20 | @classmethod 21 | def read_username(cls, filename=filename): 22 | username = None 23 | try: 24 | with open(filename) as in_file: 25 | for line in in_file.readlines(): 26 | username = line.strip() or username 27 | except IOError: 28 | pass 29 | return username or 'Player 1' 30 | 31 | def will_close(self): 32 | self.username = self['namefield'].text.strip() or self.username 33 | if self.username: 34 | with open(filename, 'w') as out_file: 35 | out_file.write(self.username) 36 | 37 | root_view = GetUsernameView() 38 | ui.in_background(root_view.wait_modal()) 39 | print(root_view.username) 40 | -------------------------------------------------------------------------------- /ButtonView.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # See: http://omz-forums.appspot.com/pythonista/post/5774800525983744 4 | 5 | import ui 6 | 7 | class ButtonView(ui.View): 8 | def __init__(self, title=None): 9 | w, h = ui.get_screen_size() 10 | self.frame = (0, 0, w, h) 11 | self.counter = 0 12 | self.add_subview(self.make_button(title)) 13 | 14 | def action(self, sender): 15 | self.counter += 1 16 | sender.title = str(self.counter) 17 | 18 | def make_button(self, title=None): 19 | button = ui.Button(title=title or 'Tap me!') 20 | button.action = self.action 21 | button.flex = 'WH' 22 | button.frame = self.bounds 23 | return button 24 | 25 | if __name__ == '__main__': 26 | single_button = False 27 | 28 | if single_button: 29 | ButtonView().present() 30 | else: 31 | import string 32 | view = ui.View() 33 | view.present() 34 | buttons_per_row = 13 35 | buttons_per_col = 2 36 | w = view.width / buttons_per_row 37 | h = view.height / buttons_per_col 38 | for i, letter in enumerate(string.ascii_lowercase): 39 | button_view = ButtonView(title=letter) 40 | #button_view.border_width = 1 41 | #button_view.border_color = 'white' 42 | button_view.frame = (i % buttons_per_row * w, i // buttons_per_row * h, w, h) 43 | view.add_subview(button_view) 44 | 45 | -------------------------------------------------------------------------------- /TimedRefreshView.py: -------------------------------------------------------------------------------- 1 | # see: http://omz-forums.appspot.com/pythonista/post/6170731750621184 2 | # code: https://github.com/cclauss/Pythonista_ui/blob/master/TimedRefreshView.py 3 | 4 | import datetime, threading, ui 5 | 6 | class TimedRefreshView(ui.View): 7 | def __init__(self): 8 | self.width = 300 9 | self.add_subview(self.make_button()) 10 | self.add_subview(self.make_textview()) 11 | self.present('sheet') 12 | self.refresh_cycle() 13 | 14 | def refresh_action(self, sender): 15 | print('refresh_action({}, {})'.format(self, sender)) 16 | self['textfield'].text = str(datetime.datetime.now()) 17 | 18 | def refresh_cycle(self): 19 | if self.on_screen: 20 | print('tick') 21 | self.refresh_action(None) 22 | refresh_thread = threading.Timer(2, self.refresh_cycle).run() 23 | 24 | def make_button(self): 25 | button = ui.Button(name='button', title='Refresh time') 26 | button.action = self.refresh_action 27 | button.width = self.width 28 | return button 29 | 30 | def make_textview(self): 31 | tf = ui.TextField(name='textfield', frame=self.bounds) 32 | offset = self['button'].height 33 | tf.y += offset 34 | tf.height -= offset 35 | tf.border_color = (0, 0, 1) 36 | tf.border_width = 2 37 | tf.alignment = ui.ALIGN_CENTER 38 | tf.text = str(datetime.datetime.now()) 39 | return tf 40 | 41 | tr_view = TimedRefreshView() 42 | -------------------------------------------------------------------------------- /ScreenshotView.py: -------------------------------------------------------------------------------- 1 | import datetime, ui 2 | 3 | class ScreenshotView(ui.View): 4 | def __init__(self): 5 | self.width = 300 6 | self.add_subview(self.make_button()) 7 | self.add_subview(self.make_textview()) 8 | self.present('sheet') 9 | 10 | def get_shapshot(self): 11 | with ui.ImageContext(self.width, self.height) as context: 12 | self.draw_snapshot() 13 | return context.get_image() 14 | 15 | def screenshot_action(self, sender): 16 | print('Saving screenshots into local files:') 17 | for i in range(3): # save three screenshots in a row 18 | now = str(datetime.datetime.now()) 19 | self['textfield'].text = now 20 | filename = 'Screenshot_{}.png'.format(now.replace(' ', '_')) 21 | print('> ' + filename) 22 | with open(filename, 'wb') as out_file: 23 | out_file.write(self.get_shapshot().to_png()) 24 | 25 | def make_button(self): 26 | button = ui.Button(name='button', title='Save three screenshots') 27 | button.action = self.screenshot_action 28 | button.width = self.width 29 | return button 30 | 31 | def make_textview(self): 32 | tf = ui.TextField(name='textfield', frame=self.bounds) 33 | offset = self['button'].height 34 | tf.y += offset 35 | tf.height -= offset 36 | tf.border_color = (0, 0, 1) 37 | tf.border_width = 2 38 | tf.alignment = ui.ALIGN_CENTER 39 | tf.text = str(datetime.datetime.now()) 40 | return tf 41 | 42 | view = ScreenshotView() 43 | -------------------------------------------------------------------------------- /button_copy.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # See: http://omz-forums.appspot.com/pythonista/post/5032924382494720 3 | 4 | import console, copy, ui 5 | 6 | def button_action(sender): 7 | console.hud_alert(sender.title + ' button pressed.') 8 | 9 | #def copy_button(in_button): 10 | # button = copy.copy(in_button) # or copy.deepcopy(in_button) 11 | # for attr in dir(in_button): 12 | # if attr.startswith('_') or attr == 'superview': 13 | # continue 14 | # value = in_button.__getattribute__(attr) 15 | # if value and (attr == 'action' or not callable(value)): 16 | # #print(attr, value) 17 | # button.__setattr__(attr, value) 18 | # return button 19 | 20 | def copy_widget(in_widget): 21 | widget = copy.copy(in_widget) # or copy.deepcopy(in_button) 22 | for attr in dir(in_widget): 23 | if not attr.startswith('_'): 24 | value = in_widget.__getattribute__(attr) 25 | if value: 26 | try: 27 | widget.__setattr__(attr, value) 28 | except AttributeError as e: 29 | #print(e, attr, value) 30 | pass 31 | return widget 32 | 33 | view = ui.load_view() 34 | for i, color in enumerate('red green cyan steelblue grey black'.split()): 35 | #button = copy_button(view['Blue']) 36 | button = copy_widget(view['Blue']) 37 | # now customize the copy 38 | button.bg_color = 'pink' if color == 'red' else 'light'+color 39 | button.border_color = button.tint_color = color 40 | button.name = button.title = color.title() 41 | button.y += (i + 1) * 50 42 | view.add_subview(button) 43 | view.present() 44 | -------------------------------------------------------------------------------- /colors_of_the_week.py: -------------------------------------------------------------------------------- 1 | # See: http://omz-forums.appspot.com/pythonista/post/5819625086386176 2 | 3 | import calendar, ui 4 | 5 | ''' 6 | the colors corresponding to days of the week thai people often wear 7 | these colors on the day of the week. Is related to the Royal Family. 8 | Currently, the most important is yellow, the The King of Thailand 9 | was born on a Monday. He is the longest severing and living monach 10 | in the world. 11 | ''' 12 | 13 | _day_color_dict = {day: color for day, color in zip(calendar.day_name, 14 | 'yellow pink green orange blue purple red'.split())} 15 | 16 | for day in calendar.day_name: # just for debugging 17 | print('{}: {}'.format(day, _day_color_dict[day])) 18 | 19 | class MyTableViewDataSource (object): 20 | def tableview_cell_for_row(self, tableview, section, row): 21 | day = calendar.day_name[row] 22 | color = _day_color_dict[day] 23 | # 'subtitle'-style cells come with a built-in secondary label 24 | cell = ui.TableViewCell('subtitle') 25 | cell.background_color = 'black' 26 | cell.text_label.text = day 27 | cell.text_label.text_color = 'white' 28 | cell.detail_text_label.text = color 29 | cell.detail_text_label.text_color = color 30 | return cell 31 | 32 | def tableview_number_of_rows(self, tableview, section): 33 | return 7 # or len([x for x in calendar.day_name]) 34 | 35 | if __name__ == '__main__': 36 | tb = ui.TableView() 37 | tb.name = 'Colors of the Week' 38 | tb.background_color = 'black' 39 | tb.size_to_fit() 40 | tb.data_source = MyTableViewDataSource() 41 | tb.present('sheet') 42 | -------------------------------------------------------------------------------- /ScreenshotWebView.py: -------------------------------------------------------------------------------- 1 | import clipboard, ui 2 | 3 | html = ''' 4 | 5 | my html 6 | 7 | 8 |

h1

9 |

h2

10 |

h3

11 | This is bold text. 12 | 13 | 14 | 15 | 16 | ''' 17 | 18 | class ScreenshotView(ui.View): 19 | def __init__(self): 20 | self.present() 21 | self.add_subview(self.make_button()) 22 | #self.add_subview(self.make_webview('http://omz-software.com')) 23 | self.add_subview(self.make_webview(html)) 24 | 25 | def get_shapshot(self): 26 | with ui.ImageContext(self.width, self.height) as context: 27 | self['webview'].draw_snapshot() 28 | return context.get_image() 29 | 30 | def screenshot_action(self, sender): 31 | print('Saving a screenshot to the clipboard.') 32 | clipboard.set_image(self.get_shapshot()) 33 | 34 | def make_button(self): 35 | button = ui.Button(name='button', title='Save a screenshot to the clipboard') 36 | button.action = self.screenshot_action 37 | button.width = self.width 38 | return button 39 | 40 | #def make_webview(self, url='http://apple.com'): 41 | # wv = ui.WebView(name='webview', title=url, frame=self.bounds) 42 | # wv.load_url(url) 43 | def make_webview(self, html=html): 44 | wv = ui.WebView(name='webview', title='ScreenshotWebView', frame=self.bounds) 45 | wv.load_html(html) 46 | offset = self['button'].height 47 | wv.y += offset 48 | wv.height -= offset 49 | wv.border_color = (0, 0, 1) 50 | wv.border_width = 2 51 | return wv 52 | 53 | view = ScreenshotView() 54 | -------------------------------------------------------------------------------- /morse_code.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # See: http://en.wikipedia.org/wiki/Morse_code 4 | 5 | alpha2code_dict = { 6 | 'a' : '.-', 7 | 'b' : '-...', 8 | 'c' : '-.-.', 9 | 'd' : '-..', 10 | 'e' : '.', 11 | 'f' : '..-.', 12 | 'g' : '--.', 13 | 'h' : '....', 14 | 'i' : '..', 15 | 'j' : '.---', 16 | 'k' : '-.-', 17 | 'l' : '.-..', 18 | 'm' : '--', 19 | 'n' : '-.', 20 | 'o' : '---', 21 | 'p' : '.--.', 22 | 'q' : '--.-', 23 | 'r' : '.-.', 24 | 's' : '...', 25 | 't' : '-', 26 | 'u' : '..-', 27 | 'v' : '...-', 28 | 'w' : '.--', 29 | 'x' : '-..-', 30 | 'y' : '-.--', 31 | 'z' : '--..', 32 | '1' : '.----', 33 | '2' : '..---', 34 | '3' : '...--', 35 | '4' : '....-', 36 | '5' : '.....', 37 | '6' : '-....', 38 | '7' : '--...', 39 | '8' : '---..', 40 | '9' : '----.', 41 | '0' : '-----' } 42 | code2alpha_dict = {v:k for k,v in alpha2code_dict.items()} 43 | 44 | def morse(msg='... --- ...' or 'sos'): 45 | msg = msg.lower().strip() 46 | if msg and msg[0] not in '.-': # easy case: alpha to morse code 47 | return ' '.join([alpha2code_dict.get(x, ' ') for x in msg]) 48 | 49 | # harder case: morse code to alpha 50 | out_msg = '' 51 | while msg: 52 | letter, _, msg = msg.partition(' ') 53 | out_msg += code2alpha_dict.get(letter, ' ') 54 | return out_msg.strip() 55 | 56 | def test_cases(): 57 | print(morse('sos')) 58 | print(morse('... --- ...')) 59 | 60 | msg = 'I really LOVE hacking Morse Code in Python!' # deal with uppercase, extra spaces, punctuation 61 | print(morse(msg)) 62 | print(morse(morse(msg))) 63 | 64 | msg = ''.join([k for k in sorted(alpha2code_dict)]) 65 | print(morse(morse(msg))) # test entire translation table 66 | assert msg == morse(morse(msg)), 'Error in translation!!' 67 | 68 | def main(args): 69 | if args: 70 | print(morse(' '.join(args))) 71 | else: 72 | test_cases() 73 | 74 | if __name__ == "__main__": 75 | import sys # put quotes around morse code on commandline or words will run together 76 | main(sys.argv[1:]) # strip off the script name 77 | -------------------------------------------------------------------------------- /MorseView.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Source code: https://github.com/cclauss/Pythonista_ui 4 | # See: http://omz-forums.appspot.com/pythonista/post/4959204154540032 5 | # See: http://en.wikipedia.org/wiki/Morse_code 6 | 7 | import morse_code, toggle_flashlight, time, ui 8 | 9 | dot_secs = 0.2 # time a dot is lit up, also dark time between each dot or dash 10 | dash_secs = dot_secs * 3 # time a dash is lit up, also dark time between each letter 11 | word_secs = dot_secs * 7 # dark time between words 12 | 13 | morse_dict = { 14 | '.' : (True, dot_secs), 15 | '-' : (True, dash_secs), 16 | ' ' : (False, dash_secs-dot_secs), 17 | ',' : (False, word_secs-dot_secs) } 18 | valid_chars = ''.join(morse_dict.keys()) 19 | 20 | @ui.in_background 21 | def flash_message(msg='--- ... ---'): 22 | msg = msg.strip().replace(' ', ',') # mark word boundries with , 23 | assert msg, 'No message entered!!' 24 | assert not msg.strip(valid_chars), 'illegal morse code: ' + msg.strip(valid_chars) 25 | for c in msg: 26 | light_on, duration = morse_dict[c] 27 | if light_on: toggle_flashlight.toggle_flashlight() # light on 28 | time.sleep(duration) 29 | if light_on: toggle_flashlight.toggle_flashlight() # light off 30 | time.sleep(dot_secs) 31 | 32 | class MorseView(ui.View): 33 | def __init__(self): 34 | self.width, _ = ui.get_screen_size() 35 | self.add_subview(self.make_button()) 36 | self.add_subview(self.make_textview()) 37 | self.present('sheet') 38 | 39 | def morse_action(self, sender): 40 | msg = morse_code.morse(self['textfield'].text) 41 | assert msg, 'No message!!' 42 | self['textfield'].text = msg 43 | flash_message(msg) 44 | 45 | def make_button(self): 46 | button = ui.Button(name='button', title='Send message') 47 | button.action = self.morse_action 48 | button.width = self.width 49 | return button 50 | 51 | def make_textview(self): 52 | tf = ui.TextField(name='textfield', frame=self.bounds) 53 | offset = self['button'].height 54 | tf.y += offset 55 | tf.height -= offset 56 | tf.border_color = (0, 0, 1) 57 | tf.border_width = 2 58 | tf.alignment = ui.ALIGN_CENTER 59 | tf.text = 'This is a test!' 60 | return tf 61 | 62 | def main(args): 63 | view = MorseView() 64 | 65 | if __name__ == "__main__": 66 | import sys # put quotes around morse code on commandline or words will run together 67 | main(sys.argv[1:]) # strip off the script name 68 | -------------------------------------------------------------------------------- /pyui_embed.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | ''' 4 | Pythonista .pyui files ease coding but can be difficult to post to GitHub, 5 | etc. so this script converts a .pyui file into a standalone .py file. 6 | 7 | * Ask the user to select a .pyui file 8 | * Create a new .py file which embeds the ui element list from .pyui file 9 | * Put onto the clipobard the Python code snippet to load the pyui's view 10 | 11 | my_file.py: a user-created Python script 12 | my_file.pyui: a user-created file of Pythonista ui elements 13 | my_file_pyui.py: created by this script as a standalone .py file 14 | You can run this script to fine tune the ui elements. 15 | 16 | This script puts a few lines of code onto the clipboard which can be 17 | pasted into my_file.py so that the two .py files will work together to 18 | load the view without requiring the .pyui file. 19 | 20 | NOTE: Requires dialogs module that is only in Pythonista v1.6 and later 21 | ''' 22 | 23 | import clipboard, dialogs, json, os, pprint, ui 24 | 25 | fmt = '''# coding: utf-8 26 | 27 | # This file was generated by pyui_embed.py 28 | # Source at: https://github.com/cclauss/Pythonista_ui 29 | 30 | # See: https://omz-forums.appspot.com/pythonista/post/5771571582992384 31 | # and: https://omz-forums.appspot.com/pythonista/post/5254558653612032 32 | # and: https://omz-forums.appspot.com/editorial/post/6075976853225472 33 | 34 | import json, tempfile, ui 35 | 36 | _ui_list = {} 37 | 38 | def load_view_from_list(view_list=_ui_list): 39 | with tempfile.NamedTemporaryFile(suffix='.pyui') as temp_file: 40 | json.dump(view_list, temp_file) 41 | temp_file.seek(0) # move the file read cursor back to byte zero 42 | return ui.load_view(temp_file.name) 43 | 44 | # optional utility function to your changes to the original .pyui file 45 | def write_back_to_pyiu_file(view_list=_ui_list): 46 | filename = __file__.rpartition('_')[0] + '_xx.pyui' 47 | with open(filename, 'w') as out_file: 48 | json.dump(view_list, out_file) 49 | 50 | if __name__ == '__main__': 51 | # write_back_to_pyiu_file() 52 | view = load_view_from_list(_ui_list) 53 | view.present(hide_title_bar=True) 54 | ''' 55 | 56 | def get_pyui_from_user(): 57 | pyui_files = [f for f in os.listdir(os.curdir) if f.endswith('.pyui')] 58 | return dialogs.list_dialog(title='Pick a file', items=pyui_files) 59 | 60 | filename = get_pyui_from_user() 61 | with open(filename) as in_file: 62 | ui_list = json.load(in_file) 63 | filename = filename.replace('.', '_') + '.py' # my_file.pyui --> my_file_pyui.py 64 | with open(filename, 'w') as out_file: 65 | out_file.write(fmt.format(pprint.pformat(ui_list))) 66 | clip_text = ''' 67 | import {0} 68 | view = {0}.load_view_from_list() 69 | view.present(hide_title_bar=True) 70 | '''.format(filename.split('.')[0]) 71 | clipboard.set(clip_text) 72 | print('{}\nThe clipboard now contains the code:\n{}'.format('=' * 36, clip_text)) 73 | -------------------------------------------------------------------------------- /ThreeBackticks.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Many people initially have difficulty formating their Python code so it 3 | looks good in posts to the Pythonisa Forum. http://omz-forums.appspot.com 4 | This script attempts to properly format your code with appropriate carrage 5 | returns and backticks so that it is ready to post to the forum. 6 | ''' 7 | 8 | import clipboard, console, ui 9 | 10 | fmt = '\n\n```{}\n{}\n```\n' # .format(language, post_text) 11 | 12 | def text_is_python(in_text): 13 | for line in in_text.splitlines(): 14 | line = line.partition('#')[0].strip() or '.' 15 | first_word = line.split()[0] 16 | if first_word == 'import': 17 | return True 18 | if first_word in ('class', 'def') and line.endswith('):'): 19 | return True 20 | return False 21 | 22 | class ThreeBackticksView(ui.View): 23 | def __init__(self): 24 | self.present() 25 | self.add_subview(self.make_button()) 26 | self.add_subview(self.make_label()) 27 | self.add_subview(self.make_switch()) 28 | self.add_subview(self.make_text_view()) 29 | self.textview_did_change(self['text_view']) 30 | 31 | def make_button(self): 32 | button = ui.Button(title='Format the post') 33 | button.action = lambda sender: self.do_it() 34 | button.center = self.width * .75, 50 35 | button.tint_color = 'steelblue' 36 | return button 37 | 38 | def make_label(self): 39 | label = ui.Label(name='label') 40 | label.width = 234 41 | label.text = 'Post contains Python code?' 42 | label.text_color = 'steelblue' 43 | label.center = self.width * .20, 50 44 | return label 45 | 46 | def make_switch(self): 47 | switch = ui.Switch(name='python_code', 48 | title='Post contains Python code?') 49 | switch.center = self.width * .30, 50 50 | switch.x = self['label'].x + self['label'].width 51 | return switch 52 | 53 | def make_text_view(self): 54 | text_view = ui.TextView(frame=self.bounds, name='text_view') 55 | text_view.y += 100 56 | text_view.height -= 100 57 | text_view.delegate = self 58 | text_view.text = clipboard.get() 59 | text_view.autocapitalization_type = ui.AUTOCAPITALIZE_NONE 60 | text_view.autocorrection_type = False 61 | text_view.spellchecking_type = False 62 | return text_view 63 | 64 | def textview_did_change(self, textview): 65 | self['python_code'].value = text_is_python(textview.text) 66 | 67 | def do_it(self): 68 | text = self['text_view'].text.rstrip() 69 | if text: 70 | lang = 'python' if self['python_code'].value else '' 71 | text = fmt.format(lang, text) 72 | clipboard.set(text) 73 | self['text_view'].text = text 74 | console.hud_alert('The post is now on your clipboard.') 75 | print(text) 76 | else: 77 | print('No user text.') 78 | 79 | ThreeBackticksView() 80 | -------------------------------------------------------------------------------- /burgers_and_beers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # See: https://github.com/ChamGeeks/ChamonixHackathon2015 5 | 6 | import console, requests, ui 7 | 8 | url_root = 'https://chamonix-hackathon-2015.herokuapp.com/' 9 | #map_url_fmt = 'http://maps.apple.com/?q={name}&sll={location[lat]},{location[long]}' 10 | #map_url_fmt = 'http://maps.google.com/?daddr={location[lat]},{location[long]}' 11 | map_url_fmt = 'http://maps.google.com/?q={location[lat]},{location[long]}' 12 | offers_fmt = '{extra_info}: {type} on {day}s from {starts} til {ends}' 13 | 14 | def get_dataset(dataset='offers'): 15 | try: 16 | return requests.get(url_root + dataset).json() 17 | except requests.ConnectionError as err: 18 | console.hud_alert('No Internet access!!') 19 | exit(err) 20 | 21 | def get_bars_dict(): 22 | return {x['name']: x for x in get_dataset('bars')} 23 | 24 | def get_offers_for_bar(bar_name='Bard Up'): 25 | return (x for x in get_dataset('offers') if x['bar']['name'] == bar_name) 26 | 27 | def get_ui_image(image_url): 28 | img = ui.Image.from_data(requests.get(image_url).content) 29 | return img.with_rendering_mode(ui.RENDERING_MODE_ORIGINAL) if img else None 30 | 31 | def make_web_view_from_url(url): 32 | web_view = ui.WebView() 33 | web_view.load_url(url) 34 | return web_view 35 | 36 | class BarView(ui.View): 37 | def __init__(self, bar_dict): 38 | self.name = bar_dict['name'] 39 | self.add_subview(make_web_view_from_url(bar_dict['image_url'])) 40 | self.add_subview(make_web_view_from_url(map_url_fmt.format(**bar_dict))) 41 | self.add_subview(self.make_offers_view(self.name)) 42 | 43 | def layout(self): 44 | x, y, w, h = self.bounds 45 | half_w, half_h = w / 2, h / 2 46 | self.subviews[0].frame = x, y, half_w, half_h # image 47 | self.subviews[1].frame = half_w, y, half_w, h # map 48 | self.subviews[2].frame = x, half_h, half_w, half_h # offers 49 | 50 | def make_offers_view(self, bar_name): 51 | table_view = ui.TableView() 52 | table_list = (offers_fmt.format(**x).replace('None: ', '') for x in 53 | get_offers_for_bar(bar_name)) 54 | table_view.data_source = lds = ui.ListDataSource(table_list) 55 | lds.font = ('', 10) 56 | table_view.row_height = 20 57 | table_view.delegate = self 58 | return table_view 59 | 60 | def tableview_did_select(self, tableview, section, row): 61 | console.hud_alert(tableview.data_source.items[row]) 62 | 63 | class BarsView(ui.View): 64 | def __init__(self): 65 | self.name = 'Chamonix Bars' 66 | self.bars_dict = get_bars_dict() 67 | for bar_name in sorted(self.bars_dict): 68 | self.add_subview(self.make_bar_button(bar_name)) 69 | ui.NavigationView(self).present(orientations=['landscape']) 70 | self.navigation_view.name = 'Chamonix Hackathon 2015' 71 | 72 | def button_pressed(self, sender): 73 | self.navigation_view.push_view(BarView(self.bars_dict[sender.name])) 74 | 75 | def layout(self): 76 | w, h = self.bounds[2] / 4, self.bounds[3] / 2 77 | for i, subview in enumerate(self.subviews): 78 | subview.frame = i % 4 * w, i // 4 * h, w, h 79 | 80 | def make_bar_button(self, bar_name='Bard Up'): 81 | button = ui.Button() 82 | button.action = self.button_pressed 83 | button.font = ('', 24) 84 | button.name = button.title = bar_name 85 | button.background_image = get_ui_image(self.bars_dict[bar_name]['image_url']) 86 | return button 87 | 88 | BarsView() 89 | -------------------------------------------------------------------------------- /BikeStationsView.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import datetime, json, requests, ui 4 | 5 | auth = 'userid', 'passw0rd' # <-- API userid & password go here. 6 | url = 'https://opendata.mycity.gov/get_current_BikeStationStatus_json' 7 | 8 | map_url_fmt = 'http://maps.google.com/?q={location[lat]},{location[long]}' 9 | station_dict = {'id': 1, 'location': {'lat': 43.608879, 'long': 3.8813495}} 10 | 11 | def freshness(timestamp): 12 | def pluralize(label, count): # 1