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.
Joystick UIControl ui wrapper?
-
@mcriley821, ui.View conveniently sets all its kwargs, so we can just call super() with them, no need to iterate manually. Also, defaults can be set before. tint_color we handle separately, as it is a custom property. (In general I would advice against overriding the ui.View properties, it will come back to haunt you.)
def __init__(self, tint_color='grey', **kwargs): kwargs.setdefault('background_color', 'black') super().__init__(**kwargs) ...
-
@mikael I iterate manually to catch a non-square frame. Is there a different way to make sure this happens? I feel like I also need to override the frame setter to catch it after init
-
@mcriley821, sorry, forgot to include that bit. Seems enough to check it after super(). If you want to enforce it continuously, layout() is a good option since, in addition frame, size can change via bounds, width and height.
def __init__(self, tint_color='grey', **kwargs): kwargs.setdefault('background_color', 'black') super().__init__(**kwargs) if self.frame.width != self.frame.height: raise ValueError('Joystick must be square') ...
-
@mcriley821, also makes sense to set self.stick.flex = 'WH', so that it will resize with its superview.
-
@mcriley821, taking the arithmetic capabilities of ui.Point/Vector2, the calculations can collapse to:
def touch_moved(self, touch): touch = touch.location radius_vector = [self.radius]*2 touch_vector = touch - radius_vector magnitude = abs(touch_vector) if magnitude < self.radius: self.stick.center = touch else: self.stick.center = ( touch_vector / magnitude * self.radius + radius_vector ) if self.action: self.action(self, touch_vector) return
Thank you for your patience, I think I am done.
-
@mikael I really appreciate you taking the time to help me and the improvements! I’ve already updated a bunch of stuff from your suggestions and I’m currently working on using the ‘iob:pinpoint' ionicon to add a type of texture to the ‘joystick’!
-
@mcriley821, interesting! Careful placement to get just the head of the pin?
-
@mcriley821, if you need a bit more flexibility, here’s an objc CAGradientLayer version, credits for an original axial version to @cvp:
import ui import objc_util CAGradientLayer = objc_util.ObjCClass('CAGradientLayer') class ShadedCircle(ui.View): def __init__(self, color1='#ff8484', color2='red', **kwargs): super().__init__(**kwargs) o_color1 = objc_util.UIColor.colorWithRed_green_blue_alpha_( *ui.parse_color(color1)).CGColor() o_color2 = objc_util.UIColor.colorWithRed_green_blue_alpha_( *ui.parse_color(color2)).CGColor() layer = self.objc_instance.layer() grlayer = CAGradientLayer.layer() grlayer.setType('radial') grlayer.frame = layer.bounds() grlayer.setColors_([o_color1, o_color2]) layer.insertSublayer_atIndex_(grlayer, 0) grlayer.setStartPoint(objc_util.CGPoint(0.25, 0.25)) grlayer.setEndPoint(objc_util.CGPoint(1.0, 1.0)) grlayer.locations = [0.0, 1.0] def layout(self): self.height = self.width self.corner_radius = self.width / 2 if __name__ == '__main__': v = ui.View() c = ShadedCircle( center=v.bounds.center(), flex='RTBL', ) v.add_subview(c) v.present('fullscreen', animated=False)
-
@mikael
I just subclassed a ui.View object again and did some more stuff to check for changing the frame later with layout. Had to use layout or it wasn’t resizing correctly, or changing some of the attributes to make the stick/texture of the joystick not resize.I decided to start a github repo for Pythonista and posted it in there.
https://github.com/mcriley821/Pythonista/blob/master/UIJoystick.py
-
@mcriley821 Wow, I clearly did not look hard enough initially! That's amazing!