I would like to create an interactive quiz using Pythonista. I was trying to learn how to use the scene kit but I was having trouble doing even basic operations based on the limited examples in the documentation. I would like the questions to pop up and to tap the answers. At the end of the test, all the selected answers would be saved to a .csv file and sent to an email address for further analysis. It seems like a fairly simple task and I am hoping someone has done something like this before and can help or point me in a direction where I can start. Thanks in advance.
Forum Archive
Interactive test
@RoninSage Why do you need a Scene?
It seemed the best way to use the touch screen interface of the iPad. I am open to other suggestions.
@RoninSage Check this
Marvelous help
@cvp, while I appreciate the shout-out for Gestures, it seems truly an overkill for this use case, as is Scene.
Quickest way to get this going would probably be console.alert, which lets you show a title (e.g. โQuestion #1โ), the question, and three options for answers.
If you want more fancy graphics, a custom ui.View with a TextView and Buttons is quite straightforward as well. You can use the UI designer to create the layout.
Let us know what you need to get forward.
... and if you decide to go with the custom UI.View and want to get really fancy, with questions and answers flying in and out or other effects like that, then I would recommend Scripter to make managing the animations easier.
Here is an example implementation and I hope it helps
https://gist.github.com/4e520ac2104b902b7a2394d4e7855fa3
I tried it with scene
from scene import Scene, Node, LabelNode, run
class quiz(Scene):
def __init__(self):
super().__init__()
self.pages = []
def setup(self):
self.page_now = -1
self.buttons = Node()
self.add_child(self.buttons)
temp = LabelNode("start quiz?")
temp.position = self.size/2
self.buttons.add_child(temp)
def touch_began(self, touch):
for i in self.buttons.children:
if touch.location in i.bbox:
if self.page_now < len(self.pages)-1:
self.next_page()
print("userer picked:", i.text)
else:
print("end of quiz")
def next_page(self):
self.buttons.remove_from_parent()
self.page_now += 1
question, values = self.pages[self.page_now]
page = Node()
num_of_nodes = len(values) + 1
h = self.size.h / num_of_nodes
w = self.size.w
x = self.size.w * 0.5
but = LabelNode(question, font=('Helvetica', 40))
values.append("")
for i in range(len(values)):
but.anchor_point = (0.5, 1)
but.position = (x, self.size.y - (h*i))
but.y_scale = min(w / but.size.x, h/but.size.y)
but.x_scale = min(w / but.size.x, h/but.size.y)
page.add_child(but)
but = LabelNode(values[i], font=('Helvetica', 40))
self.buttons = page
self.add_child(page)
def add_page(self, question, values):
self.pages.insert(-2, (question, values))
if __name__ == "__main__":
app = quiz()
app.add_page("do you like this code", ["yes", "no"])
app.add_page("do you understand this code", ["yes", "no", "whats code lol"])
app.add_page("if you add lots of text it gets smallergggggggggggggggggggggggggggggggggggggggg", ["oh ok, cool"])
run(app)
@enceladus & @ellie_ff1493 thanks for the suggestions but it is not really where I wanted to go with this. Below is code that is starting to get there, however, I want all of the question boxes to move onto the next question whenever one of them is selected. I am sure how to do that. Also, I have included code at the bottom that will record the answers selected into a csv file for later use, I just need help with the code that records the selections into a list. Finally, to complete everything, I would like to start the test off with the participant entering Name, Surname, phone number and email, again something I havenโt figured out yet but I am guessing will need access to the soft keyboard. The code probably needs some cleaning up as I just started working with the code that @cvp linked to earlier.
import itertools, ui, uuid
from objc_util import *
# Generate list of all fonts
def get_font_list():
UIFont = ObjCClass('UIFont')
return list(itertools.chain(*[UIFont.fontNamesForFamilyName_(str(x))
for x in UIFont.familyNames()]))
font_list = get_font_list()
for i in range(len(font_list)):
font_list[i]=str(font_list[i])
UILayoutContainerView = ObjCClass('UILayoutContainerView')
UISwipeGestureRecognizer = ObjCClass('UISwipeGestureRecognizer')
def disable_swipe_to_close(view):
v = view.objc_instance
while not v.isKindOfClass_(UILayoutContainerView.ptr):
v = v.superview()
for gr in v.gestureRecognizers():
if gr.isKindOfClass_(UISwipeGestureRecognizer.ptr):
gr.setEnabled(False)
class Gestures():
TYPE_REGULAR = 0
TYPE_FORCE = 1
TYPE_STYLUS = 4
TYPE_ANY = 8.
TAP = b'UITapGestureRecognizer'
PINCH = b'UIPinchGestureRecognizer'
ROTATION = b'UIRotationGestureRecognizer'
SWIPE = b'UISwipeGestureRecognizer'
PAN = b'UIPanGestureRecognizer'
SCREEN_EDGE_PAN = b'UIScreenEdgePanGestureRecognizer'
LONG_PRESS = b'UILongPressGestureRecognizer'
CHANGED = 2
ENDED = 3
RIGHT = 1; LEFT = 2; UP = 4; DOWN = 8
EDGE_NONE = 0; EDGE_TOP = 1; EDGE_LEFT = 2; EDGE_BOTTOM = 4; EDGE_RIGHT = 8; EDGE_ALL = 15
def __init__(self, touch_type=TYPE_REGULAR, force_threshold=0.4, retain_global_reference = True):
self.buttons = {}
self.views = {}
self.recognizers = {}
self.actions = {}
self.touches = {}
self.touch_type = touch_type
self.force_threshold = force_threshold
if retain_global_reference:
retain_global(self)
# Friendly delegate functions
def recognize_simultaneously_default(gr_name, other_gr_name):
return False
self.recognize_simultaneously = recognize_simultaneously_default
def fail_default(gr_name, other_gr_name):
return False
self.fail = fail_default
def fail_other_default(gr_name, other_gr_name):
return False
self.fail_other = fail_other_default
# ObjC delegate functions
def simplify(func, gr, other_gr):
gr_o = ObjCInstance(gr)
other_gr_o = ObjCInstance(other_gr)
if (gr_o.view() != other_gr_o.view()):
return False
gr_name = gr_o._get_objc_classname()
other_gr_name = other_gr_o._get_objc_classname()
return func(gr_name, other_gr_name)
# Recognize simultaneously
def gestureRecognizer_shouldRecognizeSimultaneouslyWithGestureRecognizer_(_self, _sel, gr, other_gr):
return self.objc_should_recognize_simultaneously(self.recognize_simultaneously, gr, other_gr)
def objc_should_recognize_simultaneously_default(func, gr, other_gr):
return simplify(func, gr, other_gr)
self.objc_should_recognize_simultaneously = objc_should_recognize_simultaneously_default
# Fail other
def gestureRecognizer_shouldRequireFailureOfGestureRecognizer_(_self, _sel, gr, other_gr):
return self.objc_should_require_failure(self.fail_other, gr, other_gr)
def objc_should_require_failure_default(func, gr, other_gr):
return simplify(func, gr, other_gr)
self.objc_should_require_failure = objc_should_require_failure_default
# Fail
def gestureRecognizer_shouldBeRequiredToFailByGestureRecognizer_(_self, _sel, gr, other_gr):
return self.objc_should_fail(self.fail, gr, other_gr)
def objc_should_fail_default(func, gr, other_gr):
return simplify(func, gr, other_gr)
self.objc_should_fail = objc_should_fail_default
# Delegate
try:
PythonistaGestureDelegate = ObjCClass('PythonistaGestureDelegate')
except:
PythonistaGestureDelegate = create_objc_class('PythonistaGestureDelegate',
superclass=NSObject,
methods=[
gestureRecognizer_shouldRecognizeSimultaneouslyWithGestureRecognizer_,
gestureRecognizer_shouldRequireFailureOfGestureRecognizer_,
gestureRecognizer_shouldBeRequiredToFailByGestureRecognizer_],
classmethods=[],
protocols=['UIGestureRecognizerDelegate'],
debug=True)
self._delegate = PythonistaGestureDelegate.new()
@on_main_thread
def add_tap(self, view, action, number_of_taps_required = None, number_of_touches_required = None):
''' Call `action` when a tap gesture is recognized for the `view`.
Additional parameters:
* `number_of_taps_required` - Set if more than one tap is required for the gesture to be recognized.
* `number_of_touches_required` - Set if more than one finger is required for the gesture to be recognized.
'''
recog = self._get_recog('UITapGestureRecognizer', view, self._general_action, action)
if number_of_taps_required:
recog.numberOfTapsRequired = number_of_taps_required
if number_of_touches_required:
recog.numberOfTouchesRequired = number_of_touches_required
return recog
@on_main_thread
def add_screen_edge_pan(self, view, action, edges):
''' Call `action` when a pan gesture starting from the edge is recognized for the `view`. This is a continuous gesture.
`edges` must be set to one of `Gestures.EDGE_NONE/EDGE_TOP/EDGE_LEFT/EDGE_BOTTOM/EDGE_RIGHT/EDGE_ALL`. If you want to recognize pans from different edges, you have to set up separate recognizers with separate calls to this method.
Handler `action` receives the same gesture-specific attributes in the `data` argument as pan gestures, see `add_pan`.
'''
recog = self._get_recog('UIScreenEdgePanGestureRecognizer', view, self._pan_action, action)
recog.edges = edges
return recog
def _get_recog(self, recog_name, view, internal_action, final_handler):
view.touch_enabled = True
button = ui.Button()
key = str(uuid.uuid4())
button.name = key
button.action = internal_action
self.buttons[key] = button
self.views[key] = view
recognizer = ObjCClass(recog_name).alloc().initWithTarget_action_(button, sel('invokeAction:')).autorelease()
self.recognizers[key] = recognizer
self.actions[key] = final_handler
ObjCInstance(view).addGestureRecognizer_(recognizer)
recognizer.delegate = self._delegate
return recognizer
class Data():
def __init__(self):
self.recognizer = self.view = self.location = self.state = self.number_of_touches = self.scale = self.rotation = self.velocity = None
def _context(self, button):
key = button.name
(view, recog, action) = (self.views[key], self.recognizers[key], self.actions[key])
data = Gestures.Data()
data.recognizer = recog
data.view = view
data.location = self._location(view, recog)
data.state = recog.state()
data.number_of_touches = recog.numberOfTouches()
#data.additional_touch_data = self.touches[recog]
return (data, action)
def _location(self, view, recog):
loc = recog.locationInView_(ObjCInstance(view))
return ui.Point(loc.x, loc.y)
def _general_action(self, sender):
(data, action) = self._context(sender)
action(data)
def _pan_action(self, sender):
(data, action) = self._context(sender)
trans = data.recognizer.translationInView_(ObjCInstance(data.view))
vel = data.recognizer.velocityInView_(ObjCInstance(data.view))
data.translation = ui.Point(trans.x, trans.y)
data.velocity = ui.Point(vel.x, vel.y)
action(data)
action(data)
# TESTING AND DEMONSTRATION
if __name__ == "__main__":
import math, random
g = Gestures()
def random_background(view):
colors = [(0.3,0.31,0.32), (0.25,0.26,0.27), (0.2,0.21,0.22), (0.15,0.16,0.17), (0.35,0.36,0.37), (0.4,0.41,0.42), (0.45,0.46,0.47), (0.5,0.51,0.52)]
view.background_color = random.choice(colors)
view.text_color = (0,0,0) if sum(view.background_color[:3]) > 1.5 else (1,1,1) # colour of font after press button
def update_text(l, text):
l.text = text
q1_count=-1
def generic_handler1(data):
random_background(data.view)
global q1_count
q1_count+=1
if q1_count<=len(Questions1)-1:
update_text(data.view,Questions1[q1_count])
else:
update_text(data.view,'done')
q2_count=-1
def generic_handler2(data):
random_background(data.view)
global q2_count
q2_count+=1
if q2_count<=len(Questions2)-1:
update_text(data.view,Questions2[q2_count])
else:
update_text(data.view,'done')
q3_count=-1
def generic_handler3(data):
random_background(data.view)
global q3_count
q3_count+=1
if q3_count<=len(Questions3)-1:
update_text(data.view,Questions3[q3_count])
else:
update_text(data.view,'done')
q4_count=-1
def generic_handler4(data):
random_background(data.view)
global q4_count
q4_count+=1
if q4_count<=len(Questions4)-1:
update_text(data.view,Questions4[q4_count])
else:
update_text(data.view,'done')
def pan_handler(data):
update_text(data.view, 'done')
random_background(data.view)
bg.close()
Questions1=['What are my strengths?','What frustrates me?','What do I prefer to wear?','What does my desk look like?','When making a large purchase I prioritise:','Who inspires you;']
Questions2=['ohW','nehW','erehW','yhW','tahw','woH']
Questions3=['1st ?','2nd ?','3rd ?','4th ?','5th ?','6th ?']
Questions4=['Question 1','Question 2','Question 3','Question 4','Question 5','Question 6']
bg = ui.View()
bg.present(style='default',hide_title_bar=True,title_bar_color=(198/255,50/255,108/255), title_color='Green')
disable_swipe_to_close(bg) # Use to limit access to code
bg.background_color=(198/255,50/255,108/255) # set background color to pink
edge_l = ui.Label(
text='Complete as quickly as possible',
background_color='grey',
text_color='white',
alignment=ui.ALIGN_CENTER,
number_of_lines=0,
frame=(
0, 0, bg.width, 136 # position of top box (0 in, 0 down, bg.width wide, 136 high)
))
edge_l.font=(font_list[71], 40) # font type and size
bg.add_subview(edge_l)
g.add_screen_edge_pan(edge_l, pan_handler, edges=Gestures.EDGE_RIGHT)
v = ui.ScrollView(frame=(0, 95, bg.width, bg.height)) # where grid of boxes are placed
bg.add_subview(v)
label_count = -1
def create_label(title):
global label_count
label_count += 1
label_w = 443
label_h = 300
gap = 120
label_w_with_gap = label_w + gap
label_h_with_gap = label_h + gap
labels_per_line = math.floor((v.width-2*gap)/(label_w+gap))
left_margin = (v.width - labels_per_line*label_w_with_gap + gap)/2
line = math.floor(label_count/labels_per_line)
column = label_count - line*labels_per_line
l = ui.Label(
text=title,
background_color=(95/255,96/255,98/255), # box colour
text_color=(141/255,198/255,63/255), # text colour
alignment=ui.ALIGN_CENTER,
number_of_lines=0,
frame=(
left_margin+column * label_w_with_gap,
gap+line * label_h_with_gap,
label_w, label_h
))
l.font=(font_list[32], 40) # font type and size
v.add_subview(l)
return l
tap_1 = create_label("What is included in your perfect night in? \n \n Nailing a DYI project")
tap_2 = create_label("What is included in your perfect night in? \n \n Bath, bed and a good book?")
tap_3 = create_label("What is included in your perfect night in? \n \n Doonah, snacks, movie and someone to cuddle?")
tap_4 = create_label("What is included in your perfect night in? \n \n It does'nt matter as long as the house is clean!")
g.add_tap(tap_1, generic_handler1)
g.add_tap(tap_2, generic_handler2)
g.add_tap(tap_3, generic_handler3)
g.add_tap(tap_4, generic_handler4)
# code to export results to csv file once it is the same for as res given below
import csv
res = ['x', '2', 4.7]
csvfile = 'data.csv'
with open(csvfile, "w") as output:
writer = csv.writer(output, lineterminator='\n')
for val in res:
writer.writerow([val])
@RoninSage, some suggestions:
- You do not need to compile the whole list of fonts every time - you can just set the font by the name you find in the font picker ('+' above the keyboard, on the right).
- You do not need to include the whole code for gestures, you can just
importit. - Again, you do not really need gestures for the most part, just for the edge swipe to proceed. For tapping the answers I would simplify the code a lot by just using ui.Buttons, all with
actionset to the same handler function that distinguishes which button was tapped by the name of the center. - For saving the answers, do you have a distinct need for CSV, instead of something slighly more robust?
I have created code that works and does what I want it to do, however, I feel like there is a more condensed way to do this. By creating my own keyboard I can limit the inputs that are available, I can also stop that annoying little bar coming up at the bottom of the screen.
Below the comment
# functions that define key button actions
I feel there is a lot of repetition that could be done more efficiently. Also, below the comment
# Create key buttons
again, there is also a lot of repetition. I am sure there is a simple iterative way to approach this, but I am at a loss as to how to do it.
Thanks for you help.
import ui, itertools
import numpy as np
from objc_util import *
# Define variables
# Create display, further details see theBackground.py
view=ui.View()
view.present('fullscreen', hide_title_bar=True)
view.background_color = ('lightgreen')
answers=[]
# Counters
ent_count=0
# Define functions
# function that allows you to define font as a number using font_list[]
def get_font_list():
UIFont = ObjCClass('UIFont')
return list(itertools.chain(*[UIFont.fontNamesForFamilyName_(str(x))
for x in UIFont.familyNames()]))
font_list = get_font_list()
for i in range(len(font_list)):
font_list[i]=str(font_list[i])
# function that adds all keys to the screen
def add_keyboard1(some_list):
for key in some_list:
view.add_subview(some_list[key])
# function that removes all keys from the screen
def remove_keyboard1(some_list):
for key in range(0,len(some_list)):
view.remove_subview(some_list[key])
# function to create key buttons
def create_btn(btn_label,btn_name,btn_x,btn_y,btn_width):
btn_name.bg_color = (95/255,96/255,98/255,0.8) # button colour
btn_name.tint_color=(141/255,198/255,63/255) # button font colour
btn_name.center = (btn_x+view.width*0.215, btn_y-view.height*0.1) # button position
btn_name.border_width = .5 # add border to button
btn_name.width = btn_width # button width
btn_name.height = view.width*0.04 # button height
btn_name.font = (font_list[7], 20) # button font type and size
btn_name.corner_radius = view.width * 0.002 # rounds button corners
view.add_subview(btn_name) # draw button
# functions that define key button actions
def btn1_tap(sender):
txt_fld.text=txt_fld.text +'1'
def btn2_tap(sender):
txt_fld.text=txt_fld.text +'2'
def btn3_tap(sender):
txt_fld.text=txt_fld.text +'3'
def btn4_tap(sender):
txt_fld.text=txt_fld.text +'4'
def btn5_tap(sender):
txt_fld.text=txt_fld.text +'5'
def btn6_tap(sender):
txt_fld.text=txt_fld.text +'6'
def btn7_tap(sender):
txt_fld.text=txt_fld.text +'7'
def btn8_tap(sender):
txt_fld.text=txt_fld.text +'8'
def btn9_tap(sender):
txt_fld.text=txt_fld.text +'9'
def btn0_tap(sender):
txt_fld.text=txt_fld.text +'0'
def btndel_tap(sender):
txt_fld.text=txt_fld.text[:-1]
def btnq_tap(sender):
txt_fld.text=txt_fld.text +'q'
def btnw_tap(sender):
txt_fld.text=txt_fld.text +'w'
def btne_tap(sender):
txt_fld.text=txt_fld.text +'e'
def btnr_tap(sender):
txt_fld.text=txt_fld.text +'r'
def btnt_tap(sender):
txt_fld.text=txt_fld.text +'t'
def btny_tap(sender):
txt_fld.text=txt_fld.text +'y'
def btnu_tap(sender):
txt_fld.text=txt_fld.text +'u'
def btni_tap(sender):
txt_fld.text=txt_fld.text +'i'
def btno_tap(sender):
txt_fld.text=txt_fld.text +'o'
def btnp_tap(sender):
txt_fld.text=txt_fld.text +'p'
def btnsf_tap(sender):
txt_fld.text=txt_fld.text +'๐'
def btna_tap(sender):
txt_fld.text=txt_fld.text +'a'
def btns_tap(sender):
txt_fld.text=txt_fld.text +'s'
def btnd_tap(sender):
txt_fld.text=txt_fld.text +'d'
def btnf_tap(sender):
txt_fld.text=txt_fld.text +'f'
def btng_tap(sender):
txt_fld.text=txt_fld.text +'g'
def btnh_tap(sender):
txt_fld.text=txt_fld.text +'h'
def btnj_tap(sender):
txt_fld.text=txt_fld.text +'j'
def btnk_tap(sender):
txt_fld.text=txt_fld.text +'k'
def btnl_tap(sender):
txt_fld.text=txt_fld.text +'l'
def btnhf_tap(sender):
txt_fld.text=txt_fld.text +'๐'
def btnz_tap(sender):
txt_fld.text=txt_fld.text +'z'
def btnx_tap(sender):
txt_fld.text=txt_fld.text +'x'
def btnc_tap(sender):
txt_fld.text=txt_fld.text +'c'
def btnv_tap(sender):
txt_fld.text=txt_fld.text +'v'
def btnb_tap(sender):
txt_fld.text=txt_fld.text +'b'
def btnn_tap(sender):
txt_fld.text=txt_fld.text +'n'
def btnm_tap(sender):
txt_fld.text=txt_fld.text +'m'
def btnm_tap(sender):
txt_fld.text=txt_fld.text +'m'
def btnenter_tap(sender):
global ent_count
if len(txt_fld.text)!=0:
ent_count+=1
if ent_count<3: # after 3rd entry stops input
answers.append(txt_fld.text) # records all inputs
txt_fld.text='' # clears entry after enter is pressed
else:
answers.append(txt_fld.text) # records last input
view.remove_subview(txt_fld) # clears textfield
some_box.text='My time is a valuable thing' # changes box text
remove_keyboard1(all_btns) # clears all keys
view.close()
# Create key buttons
btn1 = ui.Button(title='1')
create_btn('1',btn1,view.width*0.0,view.height*0.66,view.width*0.04)
btn1.action = btn1_tap
btn2 = ui.Button(title='2')
create_btn('2',btn2,view.width*0.05,view.height*0.66,view.width*0.04)
btn2.action = btn2_tap
btn3 = ui.Button(title='3')
create_btn('3',btn3,view.width*0.1,view.height*0.66,view.width*0.04)
btn3.action = btn3_tap
btn4 = ui.Button(title='4')
create_btn('4',btn4,view.width*0.15,view.height*0.66,view.width*0.04)
btn4.action = btn4_tap
btn5 = ui.Button(title='5')
create_btn('5',btn5,view.width*0.2,view.height*0.66,view.width*0.04)
btn5.action = btn5_tap
btn6 = ui.Button(title='6')
create_btn('6',btn6,view.width*0.25,view.height*0.66,view.width*0.04)
btn6.action = btn6_tap
btn7 = ui.Button(title='7')
create_btn('7',btn7,view.width*0.3,view.height*0.66,view.width*0.04)
btn7.action = btn7_tap
btn8 = ui.Button(title='8')
create_btn('8',btn8,view.width*0.35,view.height*0.66,view.width*0.04)
btn8.action = btn8_tap
btn9 = ui.Button(title='9')
create_btn('9',btn9,view.width*0.4,view.height*0.66,view.width*0.04)
btn9.action = btn9_tap
btn0 = ui.Button(title='0')
create_btn('0',btn0,view.width*0.45,view.height*0.66,view.width*0.04)
btn0.action = btn0_tap
btndel = ui.Button(title='delete')
create_btn('delete',btndel,view.width*0.505,view.height*0.66,view.width*0.09)
btndel.action = btndel_tap
btnq = ui.Button(title='q')
create_btn('q',btnq,view.width*0.025,view.height*0.73,view.width*0.04)
btnq.action = btnq_tap
btnw = ui.Button(title='w')
create_btn('w',btnw,view.width*0.075,view.height*0.73,view.width*0.04)
btnw.action = btnw_tap
btne = ui.Button(title='e')
create_btn('e',btne,view.width*0.125,view.height*0.73,view.width*0.04)
btne.action = btne_tap
btnr = ui.Button(title='r')
create_btn('r',btnr,view.width*0.175,view.height*0.73,view.width*0.04)
btnr.action = btnr_tap
btnt = ui.Button(title='t')
create_btn('t',btnt,view.width*0.225,view.height*0.73,view.width*0.04)
btnt.action = btnt_tap
btny = ui.Button(title='y')
create_btn('y',btny,view.width*0.275,view.height*0.73,view.width*0.04)
btny.action = btny_tap
btnu = ui.Button(title='u')
create_btn('u',btnu,view.width*0.325,view.height*0.73,view.width*0.04)
btnu.action = btnu_tap
btni = ui.Button(title='i')
create_btn('i',btni,view.width*0.375,view.height*0.73,view.width*0.04)
btni.action = btni_tap
btno = ui.Button(title='o')
create_btn('o',btno,view.width*0.425,view.height*0.73,view.width*0.04)
btno.action = btno_tap
btnp = ui.Button(title='p')
create_btn('p',btnp,view.width*0.475,view.height*0.73,view.width*0.04)
btnp.action = btnp_tap
btnsf = ui.Button(title='๐')
create_btn('๐',btnsf,view.width*0.525,view.height*0.73,view.width*0.04)
btnsf.action = btnsf_tap
btna = ui.Button(title='a')
create_btn('a',btna,view.width*0.05,view.height*0.8,view.width*0.04)
btna.action = btna_tap
btns = ui.Button(title='s')
create_btn('s',btns,view.width*0.1,view.height*0.8,view.width*0.04)
btns.action = btns_tap
btnd = ui.Button(title='d')
create_btn('d',btnd,view.width*0.15,view.height*0.8,view.width*0.04)
btnd.action = btnd_tap
btnf = ui.Button(title='f')
create_btn('f',btnf,view.width*0.2,view.height*0.8,view.width*0.04)
btnf.action = btnf_tap
btng = ui.Button(title='g')
create_btn('g',btng,view.width*0.25,view.height*0.8,view.width*0.04)
btng.action = btng_tap
btnh = ui.Button(title='h')
create_btn('h',btnh,view.width*0.3,view.height*0.8,view.width*0.04)
btnh.action = btnh_tap
btnj = ui.Button(title='j')
create_btn('j',btnj,view.width*0.35,view.height*0.8,view.width*0.04)
btnj.action = btnj_tap
btnk = ui.Button(title='k')
create_btn('k',btnk,view.width*0.4,view.height*0.8,view.width*0.04)
btnk.action = btnk_tap
btnl = ui.Button(title='l')
create_btn('l',btnl,view.width*0.45,view.height*0.8,view.width*0.04)
btnl.action = btnl_tap
btnhf = ui.Button(title='๐')
create_btn('๐',btnhf,view.width*0.5,view.height*0.8,view.width*0.04)
btnhf.action = btnhf_tap
btnz = ui.Button(title='z')
create_btn('z',btnz,view.width*0.075,view.height*0.87,view.width*0.04)
btnz.action = btnz_tap
btnx = ui.Button(title='x')
create_btn('x',btnx,view.width*0.125,view.height*0.87,view.width*0.04)
btnx.action = btnx_tap
btnc = ui.Button(title='c')
create_btn('c',btnc,view.width*0.175,view.height*0.87,view.width*0.04)
btnc.action = btnc_tap
btnv = ui.Button(title='v')
create_btn('v',btnv,view.width*0.225,view.height*0.87,view.width*0.04)
btnv.action = btnv_tap
btnb = ui.Button(title='b')
create_btn('b',btnb,view.width*0.275,view.height*0.87,view.width*0.04)
btnb.action = btnb_tap
btnn = ui.Button(title='n')
create_btn('n',btnn,view.width*0.325,view.height*0.87,view.width*0.04)
btnn.action = btnn_tap
btnm = ui.Button(title='m')
create_btn('m',btnm,view.width*0.375,view.height*0.87,view.width*0.04)
btnm.action = btnm_tap
btnenter = ui.Button(title='enter')
create_btn('enter',btnenter,view.width*0.425,view.height*0.87,view.width*0.09)
btnenter.action = btnenter_tap
# List of all button names
all_btns=(btn1,btn2,btn3,btn4,btn5,btn6,btn7,btn8,btn9,btn0,btndel,
btnq,btnw,btne,btnr,btnt,btny,btnu,btni,btno,btnp,btnsf,
btna,btns,btnd,btnf,btng,btnh,btnj,btnk,btnl,btnhf,
btnz,btnx,btnc,btnv,btnb,btnn,btnm,btnenter)
# Include non-interactive box
some_box = ui.Label(
text='This is where you can ask some questions', # box text
background_color=(95/255,96/255,98/255), # box colour
text_color='white', # box text colour
alignment=ui.ALIGN_CENTER, # text alignment in box
number_of_lines=0, # number of lines used in box
frame=(0, 0, view.width, 100)) # position of top box (0 in, 0 down, view.width wide, 100 high)
some_box.font=('HoeflerText-BlackItalic', 40) # box font type and size
view.add_subview(some_box) # create box
txt_fld=ui.TextField(frame=(view.width*0.275, view.height*0.34,view.width*0.45, view.height*0.1)) # defines textfield dimensions
txt_fld.alignment=ui.ALIGN_CENTER # places text in center of the box
txt_fld.font = ('Chalkduster', 30) # change textfield font
txt_fld.text_color='hotpink' # text colour
txt_fld.enabled = False # cannot click in textfield
view.add_subview(txt_fld) # create textfield
@RoninSage already shorter ๐
import ui, itertools
import numpy as np
from objc_util import *
# Define variables
# Create display, further details see theBackground.py
view=ui.View()
view.present('fullscreen', hide_title_bar=True)
view.background_color = ('lightgreen')
answers=[]
# Counters
ent_count=0
# Define functions
# function that allows you to define font as a number using font_list[]
def get_font_list():
UIFont = ObjCClass('UIFont')
return list(itertools.chain(*[UIFont.fontNamesForFamilyName_(str(x))
for x in UIFont.familyNames()]))
font_list = get_font_list()
for i in range(len(font_list)):
font_list[i]=str(font_list[i])
# function to create key buttons
def create_btn(btn_label,btn_name,btn_x,btn_y,btn_width):
btn_name = ui.Button(title=btn_label,name=btn_name)
if btn_label == 'delete':
btn_name.action = btndel_tap
elif btn_label == 'enter':
btn_name.action = btnenter_tap
else:
btn_name.action = btn_tap
btn_name.bg_color = (95/255,96/255,98/255,0.8) # button colour
btn_name.tint_color=(141/255,198/255,63/255) # button font colour
btn_name.center = (btn_x+view.width*0.215, btn_y-view.height*0.1) # button position
btn_name.border_width = .5 # add border to button
btn_name.width = btn_width # button width
btn_name.height = view.width*0.04 # button height
btn_name.font = (font_list[7], 20) # button font type and size
btn_name.corner_radius = view.width * 0.002 # rounds button corners
view.add_subview(btn_name) # draw button
# functions that define key button actions
def btn_tap(sender):
txt_fld.text=txt_fld.text + sender.title
def btndel_tap(sender):
txt_fld.text=txt_fld.text[:-1]
def btnenter_tap(sender):
global ent_count
if len(txt_fld.text)!=0:
ent_count+=1
if ent_count<3: # after 3rd entry stops input
answers.append(txt_fld.text) # records all inputs
txt_fld.text='' # clears entry after enter is pressed
else:
answers.append(txt_fld.text) # records last input
view.remove_subview(txt_fld) # clears textfield
some_box.text='My time is a valuable thing' # changes box text
for subv in view.subviews:
if type(subv) is ui.Button:
view.remove_subview(subv)
view.close()
# Create key buttons
create_btn('1', 'btn1',view.width*0.0,view.height*0.66,view.width*0.04)
create_btn('2', 'btn2',view.width*0.05,view.height*0.66,view.width*0.04)
create_btn('3', 'btn3',view.width*0.1,view.height*0.66,view.width*0.04)
create_btn('4', 'btn4',view.width*0.15,view.height*0.66,view.width*0.04)
create_btn('5', 'btn5',view.width*0.2,view.height*0.66,view.width*0.04)
create_btn('6', 'btn6',view.width*0.25,view.height*0.66,view.width*0.04)
create_btn('7', 'btn7',view.width*0.3,view.height*0.66,view.width*0.04)
create_btn('8', 'btn8',view.width*0.35,view.height*0.66,view.width*0.04)
create_btn('9', 'btn9',view.width*0.4,view.height*0.66,view.width*0.04)
create_btn('0', 'btn0',view.width*0.45,view.height*0.66,view.width*0.04)
create_btn('delete', 'btndel',view.width*0.505,view.height*0.66,view.width*0.09)
create_btn('q', 'btnq',view.width*0.025,view.height*0.73,view.width*0.04)
create_btn('w', 'btnw',view.width*0.075,view.height*0.73,view.width*0.04)
create_btn('e', 'btne',view.width*0.125,view.height*0.73,view.width*0.04)
create_btn('r', 'btnr',view.width*0.175,view.height*0.73,view.width*0.04)
create_btn('t', 'btnt',view.width*0.225,view.height*0.73,view.width*0.04)
create_btn('y', 'btny',view.width*0.275,view.height*0.73,view.width*0.04)
create_btn('u', 'btnu',view.width*0.325,view.height*0.73,view.width*0.04)
create_btn('i', 'btni',view.width*0.375,view.height*0.73,view.width*0.04)
create_btn('o', 'btno',view.width*0.425,view.height*0.73,view.width*0.04)
create_btn('p', 'btnp',view.width*0.475,view.height*0.73,view.width*0.04)
create_btn('๐', 'btnsf',view.width*0.525,view.height*0.73,view.width*0.04)
create_btn('a', 'btna',view.width*0.05,view.height*0.8,view.width*0.04)
create_btn('s', 'btns',view.width*0.1,view.height*0.8,view.width*0.04)
create_btn('d', 'btnd',view.width*0.15,view.height*0.8,view.width*0.04)
create_btn('f', 'btnf',view.width*0.2,view.height*0.8,view.width*0.04)
create_btn('g', 'btng',view.width*0.25,view.height*0.8,view.width*0.04)
create_btn('h', 'btnh',view.width*0.3,view.height*0.8,view.width*0.04)
create_btn('j', 'btnj',view.width*0.35,view.height*0.8,view.width*0.04)
create_btn('k', 'btnk',view.width*0.4,view.height*0.8,view.width*0.04)
create_btn('l', 'btnl',view.width*0.45,view.height*0.8,view.width*0.04)
create_btn('๐', 'btnhf',view.width*0.5,view.height*0.8,view.width*0.04)
create_btn('z', 'btnz',view.width*0.075,view.height*0.87,view.width*0.04)
create_btn('x', 'btnx',view.width*0.125,view.height*0.87,view.width*0.04)
create_btn('c', 'btnc',view.width*0.175,view.height*0.87,view.width*0.04)
create_btn('v', 'btnv',view.width*0.225,view.height*0.87,view.width*0.04)
create_btn('b', 'btnb',view.width*0.275,view.height*0.87,view.width*0.04)
create_btn('n', 'btnn',view.width*0.325,view.height*0.87,view.width*0.04)
create_btn('m', 'btnm',view.width*0.375,view.height*0.87,view.width*0.04)
create_btn('enter', 'btnenter',view.width*0.425,view.height*0.87,view.width*0.09)
# Include non-interactive box
some_box = ui.Label(
text='This is where you can ask some questions', # box text
background_color=(95/255,96/255,98/255), # box colour
text_color='white', # box text colour
alignment=ui.ALIGN_CENTER, # text alignment in box
number_of_lines=0, # number of lines used in box
frame=(0, 0, view.width, 100)) # position of top box (0 in, 0 down, view.width wide, 100 high)
some_box.font=('HoeflerText-BlackItalic', 40) # box font type and size
view.add_subview(some_box) # create box
txt_fld=ui.TextField(frame=(view.width*0.275, view.height*0.34,view.width*0.45, view.height*0.1)) # defines textfield dimensions
txt_fld.alignment=ui.ALIGN_CENTER # places text in center of the box
txt_fld.font = ('Chalkduster', 30) # change textfield font
txt_fld.text_color='hotpink' # text colour
txt_fld.enabled = False # cannot click in textfield
view.add_subview(txt_fld) # create textfield
@RoninSage still shorter
# Create key buttons
def create_row(row_labels,row_x,row_y):
x = row_x
for btn_label in row_labels:
create_btn(btn_label,'btn'+btn_label, view.width*x, view.height*row_y, view.width*0.04)
x = x + 0.05
create_row('1234567890', 0.0,0.66)
create_btn('delete', 'btndel',view.width*0.505,view.height*0.66,view.width*0.09)
create_row('qwertyuiop๐', 0.025,0.73)
create_row('asdfghjkl๐', 0.05,0.8)
create_row('zxcvbnm', 0.075,0.87)
create_btn('enter', 'btnenter',view.width*0.425,view.height*0.87,view.width*0.09)
@RoninSage Additional infos
- button name is not needed because we remove them by sub_view of view
@cvp perfect, that simplified all the repetions and I still understand what is going on, thanks heaps.