Forum Archive

How to get key pressing in scene module?

yaley

Hi, i am a newbie and i have a problem.
In my project, cannot detect am i pressing the key, if use ui.View.get_key_commands() and ui.View.key_command, it can only detect the keypress, not keydown

So how???

JonB

https://www.swiftbysundell.com/tips/handling-keyup-and-keydown-events/

This is now possible in iOS 13.4, however, someone will need to translate this to ObjC. @cvp? @mikael? I can give it a shot, but not until next week.

yaley

sorry, but that website was JS or Java…..

JonB

It is swift, but it is possible to convert that to objc_util

cvp

@JonB I would like to help but it seems to be a little bit too complex for me, I guess it is better to wait that you find the time to do it. Sorry, but happy to discover this new feature from Apple

JonB

I believe we just need to have a custom view controller that implements

pressesBegan_withEvent(presses, event)

Where presses is an NSSet of UIPresses. You can use presses.allObjects() to convert a NSSet to array...from.there, key.

JonB

E.g. presses.allObjects()[0].key().keyCode()

yaley

@JonB what is pressesBegan_withEvent(presses, event)

cvp

@yaley said

what is pressesBegan_withEvent(presses, event)

A method to implement to the custom UIViewController, this is a new standard method called when a key goes down

yaley

@cvp so the whole code is like…. objc.ObjCClass("UIViewController").pressBegan_withEvent(presses, event)
that?

cvp

@yaley Ho no. This is not so simple. You have to create a custom UIViewController and configure the used methods and protocols. And one method could be the new one which should be called at each key down

You could check this topic where a such ViewController is used with a user method CustomViewController_touchesBegan_withEvent_

Only to show it is not so easy

yaley

@cvp ok Thanks for your helping!

cvp

@yaley Sorry that I can't help more...

yaley

@cvp oh sorry, i have another question, what are presses and event?
Is presses like [“a”, “b”, “c”] and is event like a function?

cvp

@yaley presses is a set of UIPress objects and event is an UIPressesEvent object. Both are ObjectiveC classes

cvp

@yaley see Apple doc UIPressesEvent and UIPress

yaley

@cvp thank you ❤️ you helped me a lot

cvp

@yaley I hoped that some code like this could work, but not true. Thus, sure this is too complex for me. We will wait availability of @JonB

from objc_util import *
import ui

UIViewController = ObjCClass('UIViewController')

# Some callback definitions used by create_objc_class
def pressesBegan_withEvent(_cmd, _presses, _event):
    print('pressesBegan_withEvent') 
    presses = ObjCInstance(_presses)

# The main class...
class MyView(ui.View):
    def __init__(self):
        super().__init__(self)
        self.background_color = 'gray'

    @on_main_thread
    def initialize(self):

        v = ui.View()
        v.frame = self.frame
        tf = ui.TextField()
        tf.frame = (10,10,200,32)
        v.add_subview(tf)
        vo = ObjCInstance(v)
        #print(vo)
        #print(dir(vo))

        # set up the custom view controller
        methods = [pressesBegan_withEvent]
        protocols = []
        CustomViewController = create_objc_class('CustomViewController', UIViewController, methods=methods, protocols=protocols)
        cvc = CustomViewController.alloc().init()
        cvc.view = vo

        # internal scheming...
        self_objc = ObjCInstance(self)
        self_objc.nextResponder().addChildViewController_(cvc)
        self_objc.addSubview_(vo)
        cvc.didMoveToParentViewController_(self_objc)

        tf.begin_editing()


if __name__ == '__main__':
    v = MyView()
    v.present('fullscreen')
    v.initialize()
yaley

@cvp it doesn’t work….. but really thanks for your helping

cvp

@yaley said

it doesn’t work

That's what I said with "it is not true" and I'm sorry for you but I'm sure that our guru, read @JonB, will solve that as soon he is available (dear John, sorry to put pressure on you)

JonB

I don't have an iOS device with me for a few more days...but I suspect this only works with Bluetooth keyboard, and in view controllers without textfields. If you begin_editing, the textfields will become first responder, and steal any keystrokes.

cvp

@JonB I've tried with Bluetooth keyboard (my Mac's one) and with another view controller without TextField, without success (this script where I've added this method

# Some callback definitions used by create_objc_class
def pressesBegan_withEvent(_cmd, _presses, _event):
    print('pressesBegan_withEvent') 
    presses = ObjCInstance(_presses)

.
.
.
        methods = [share, goBack, goForward, searchBarSearchButtonClicked_, pressesBegan_withEvent]
cvp

@JonB said

I don't have an iOS device with me for a few more days

No stress, enjoy a life without iDevice

yaley

@cvp Can you make me an working example, please?

cvp

@yaley unhappily no, sorry, after multiple tries, that's why I say we need help from @JonB as soon he his available (with an iOS device, of course)

cvp

@JonB I also have tried this code, always without success

from objc_util import *
import ui

UIViewController = ObjCClass('UIViewController')

# Some callback definitions used by create_objc_class
def pressesBegan_withEvent(_cmd, _presses, _event):
    print('pressesBegan_withEvent') 
    presses = ObjCInstance(_presses)

# The main class...
class MyView(ui.View):
    def __init__(self):
        super().__init__(self)
        self.background_color = 'gray'

    @on_main_thread
    def initialize(self):

        # set up the custom view controller
        methods = [pressesBegan_withEvent]
        protocols = []
        CustomViewController = create_objc_class('CustomViewController', UIViewController, methods=methods, protocols=protocols)
        cvc = CustomViewController.alloc().init()



        #The first thing you'll need is a view controller you can present from. In most cases, the root view controller of the key window will do. You can get it like this:

        root_vc = UIApplication.sharedApplication().keyWindow().rootViewController()
        #Note however that presenting from the root view controller will fail if it is already presenting some other view controller. This may be the case if the settings are shown or something like that. You could get around it like this:

        while root_vc.presentedViewController():
            root_vc = root_vc.presentedViewController()
        #Once you have a presenting view controller, you can call presentViewController_animated_completion_:


        root_vc.presentViewController_animated_completion_(cvc, True, None)
        #For now, just always pass None for the completion parameter. animated can be True or False, depending on whether you want a transition animation.

        #You can tweak the presentation by setting your view controller's modalPresentationStyle and modalTransitionStyle properties. E.g. to present the view controller in the "form sheet" style, use

        cvc.setModalPresentationStyle_(2)

if __name__ == '__main__':
    v = MyView()
    v.present('fullscreen')
    v.initialize()
JonB

@cvp I think the signature needs to include TWO dummy arguments:

pressesBegan_withEvent(_self, _sel, presses, Event)

What happens if you create a generic responder, with no overloaded methods, then check the .encoding of that method, along with beginTouches_withEvent, which should look thr same.

cvp

@JonB said

pressesBegan_withEvent(_self, _sel, presses, Event)

I had already tried that but it gives an error

ValueError: <function pressesBegan_withEvent at 0x117d28158> has 4 arguments (expected 3)
cvp

@JonB said

What happens if you create a generic responder, with no overloaded methods, then check the .encoding of that method, along with beginTouches_withEvent, which should look thr same.

Sorry, but I don't know how to do that

JonB

@cvp what about if you try

def pressesBegan_withEvent_(_self, _cmd, presses, Event):

(Note trailing underscore)

Also, in console, try checking

cvc.touchesBegan_withEvent_.encoding

Then compare that to your new responder's pressesBegan_withEvent_.encoding

cvp

@JonB

def pressesBegan_withEvent_(_self, _cmd, presses, Event):

is not refused but not called when I press a key on my Bluetooth keyboard

        print(cvc.touchesBegan_withEvent_.encoding)

Gives b'v32@0:8@16@24'

        print(cvc.pressesBegan_withEvent_.encoding)

Gives the same

yaley

@JonB @cvp thanks for your helping and I will never forget your helpings

yaley

@JonB do you have your ios device now?

yaley

@cvp @JonB
Finally!!!
i run this code with SUCCESS!
thank you for your helpings!

from objc_util import *
import ui

UIViewController = ObjCClass('UIViewController')


# Some callback definitions used by create_objc_class
def CustomViewController_pressesBegan_withEvent_(_self, _cmd, _presses, _event):
    print('pressesBegan_withEvent')
    presses = ObjCInstance(_presses)


# The main class...
class MyView(ui.View):
    def __init__(self):
        super().__init__(self)
        self.background_color = 'gray'

    @on_main_thread
    def init(self):
        v = ui.View()
        v.frame = self.frame
        tf = ui.TextField()
        tf.frame = (10, 10, 200, 32)
        v.add_subview(tf)
        vo = ObjCInstance(v)
        #print(vo)
        #print(dir(vo))

        # set up the custom view controller
        methods = [CustomViewController_pressesBegan_withEvent_]
        protocols = []
        CustomViewController = create_objc_class(
            'CustomViewController',
            UIViewController,
            methods=methods,
            protocols=protocols)
        cvc = CustomViewController.alloc().init()
        cvc.view = vo

        # internal scheming...
        self_objc = ObjCInstance(self)
        self_objc.nextResponder().addChildViewController_(cvc)
        self_objc.addSubview_(vo)
        cvc.didMoveToParentViewController_(self_objc)

        tf.begin_editing()

if __name__ == '__main__':
    v = MyView()
    v.present('fullscreen')
    v.init()
JonB

I was wondering if the issue was lack of focus. Some documentation says the view must be in the responder chain of the focused view. It might be possible to do without the textfield, if we create a custom view that implements canBecomeFocused(), though I'm not sure how to then force it to take focus.
By having a textview, I guess that's taken care of, although I'm a little surprised that the textview doesn't eat the event itself.

cvp

@yaley 👏

yaley

@cvp @JonB Again thank you for helping me a lot :D i was a python coder before, and i started learn js, nodejs, html for web development. But after my friend introduced Pythonista to me, i started to code python again, but i was using pygame module before (xD) and i was puzzled when using scene module, but now, it’s not, but ofc questions cannot be avoided, so i asked you @JonB and @cvp you both helped me a lot!

( lastly, i want to tell you that i am 10 years old, i think you are confused and amazed, XD )

cvp

@yaley said

i am 10 years old

I'am 70 and at 10, I only was playing with Lego, you're an happy guy to have such tools 😂

cvp

@yaley said

i was a python coder before

as you are only 10 years old, when you say "before", do you mean in another life? Reincarnation? 😂

cvp

@JonB said

I'm a little surprised that the textview doesn't eat the event itself.

Is an ui.TextField an UIViewController? Because the called method is only for such an object