I often find that when trying to make a ui, I want to call parts of the code that eventually will be called by an interface interaction. When when building up the view and testing as I go along I don't have everything in place, so I end up creating dummy buttons or menuitems to trigger these calls so I can view the results and make sure it's working as I expect.
That's why I started on this view, below. It's far from being perfect. Just an idea, that I am also skeptical about. Maybe @JonB overlay class a better way to implement this. I tried, I just had a hard time controlling it, as it wasn't designed for this purpose specifically. Anyway, the below is an attempt. Needs to be tightened up, but it works. Just feels messy and too long.
# Pythonista Forum - @Phuket2
import ui
class DebugToolBar(ui.View):
'''
DebugToolBar
description:
A helper view (debugging), when you need to fire some functions
etc. in your interface when testing a view.
its not trying to be nice and fit in. it adds its self to the
parent view and tries to bring itself to the front.
params:
parent = target ui.View parent
h = height of the view
num_btns = the number of buttons to create
'''
def __init__(self, parent, h=44, num_btns=4, *args, **kwargs):
super().__init__(*args, **kwargs)
self.parent = parent
self.num_btns = num_btns
self.name = 'bv'
self.width = (h * num_btns)
self.height = h
self.bg_color = 'cornflowerblue'
self.alpha = .5
self.corner_radius = 4
self.x = (parent.width / 2) - (self.width / 2)
self.make_view()
self.bring_to_front()
# position the view and set the flex attr
# this view should be both size and orientation friendly.
# the view is at the bottom of the parent
self.y = parent.height - self.height
self.flex = 'lrtb'
# add the view to the parent. naughty!
parent.add_subview(self)
def make_view(self):
r = ui.Rect(*self.bounds).inset(2, 2)
h = r.height
btn_rect = ui.Rect(0, 0, h, h)
btn_rect.y = r.min_y
offset = (r.width/2) - ((self.num_btns * h) / 2)
for i in range(0, self.num_btns):
btn_rect.x = (h * i) + offset
btn = self._make_btn(str(i), btn_rect)
btn.action = self.btn_action
self.add_subview(btn)
def _make_btn(self, name, rect):
r = ui.Rect(*rect).inset(4, 4)
btn = ui.Button(name=name, frame=r)
btn.corner_radius = r.width / 2
btn.title = name
btn.bg_color = 'white'
btn.tint_color = 'black'
btn.callback = None # dynamically created attr
btn.font = ('Arial Rounded MT Bold', btn.width * .4)
return btn
def btn_action(self, sender):
# self pop_effect not done to be smart, rather to give some real
# user feedback that the btn was pressed.
# because i am always sitting in a bar with live band or music
# videos playing, i dont think about sound feedback. Really!!
self.pop_effect(v=sender)
# if set_action method is used, we save the callback func in
# a dynamically created attr 'callback'
if sender.callback:
sender.callback(sender)
return
if hasattr(self.parent, 'bv_action'):
self.parent.bv_action(sender)
else:
print('''Create a method in the class '{}' like - '{}' , if you want your parent to be called back with a btn presses'''
.format(self.parent.__class__.__name__,\
'def bv_action(self, sender)'))
def set_action(self, btn_index, func):
'''
attach a function to one of the buttons, well we fake it.
we dont attach the func directly. we add it to a
dynamically created attr obj.callback
Then the code flow remains the same, we still animate etc.
'''
if btn_index is -1:
for btn in self.subviews:
btn.callback = func
else:
try:
btn = self.subviews[btn_index]
btn.callback = func
except:
raise
def pop_effect(self, v, duration=.4, scale=1.25):
'''
pop - just used to pop a button in this case by scaling it.
aka. a visual effect applied to v
'''
x = y = scale
def a():
v.transform = ui.Transform()
v.transform = ui.Transform.scale(x, y)
ui.animate(a, duration=duration)
class MyClass(ui.View):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.make_view()
# best to call this last to make sure we end up on top
# or you could do it manually with bv.bring_to_front()
bv = DebugToolBar(self, h=60, num_btns=6)
def make_view(self):
# make the view as per normal
pass
# DebugToolBar, will call this method if it exists,
# and if set_action has not been called.
def bv_action(self, sender):
# to show how this callback could be used, silly example
if sender.name == '0':
# do something
self.bg_color = 'red'
elif sender.name == '1':
# do something
self.bg_color = 'green'
elif sender.name == '2':
# do something
self.bg_color = 'blue'
elif sender.name == '3':
# do something
self.bg_color = 'aqua'
elif sender.name == '4':
# do something
self.bg_color = 'orange'
elif sender.name == '5':
# do something
self.bg_color = 'maroon'
def func_action(sender):
# example of a func that we set with set_action
sender.superview.parent.bg_color = 'purple'
if __name__ == '__main__':
_use_theme = False
w, h = 375, 667
f = (0, 0, w, h)
style = 'sheet'
mc = MyClass(frame=f, bg_color='white')
mc.present(style=style, animated=False)
# another way to link a func to a button by btn index
# if -1 is passed, all buttons are assigned the callback func
# a more compact way, trying to reduce interference
mc['bv'].set_action(1, func_action)