[PYTHON] [Pyto] J'ai essayé d'utiliser un smartphone comme clavier pour PC

image2.jpg

introduction

Il semble que de nombreuses personnes sont incapables de taper alors que l'on dit que les jeunes sont loin des ordinateurs. Cependant, les jeunes sont habitués à manipuler des smartphones au lieu de pouvoir utiliser des PC, et la saisie rapide des lycéennes actuelles est aussi rapide que les filles du garage sont surprises.

Dans ce cas, il semble que l'on dise depuis un certain temps que l'entrée PC doit se faire par effleurement, mais aucun logiciel ne pouvait être facilement essayé.

J'ai donc créé un clavier à l'aide d'une application appelée Pyto qui permet la programmation Python sur iOS.

Produit existant

J'ai également trouvé une image où j'essayais de créer un clavier à l'aide de l'écran tactile.

environnement

[Côté IOS] iPhone SE 2020 (iOS13.4.1) Pyto (12.0.3)

[Côté PC] Windows 10 Home 64bit (1909) Python 3.7.4

Bibliothèque pyautogui (0.9.50) pyperclip (1.8.0)

spécification

Envoie les données d'entrée du terminal iOS au PC via la communication par socket.

Les fonctions sont la saisie de caractères, Entrée et Retour arrière. Il est possible d'ajouter des fonctions telles que la croix et la suppression, mais cette fois nous l'avons limité à la saisie de caractères.

La conversion Kanji est effectuée du côté iOS, et une fois la conversion confirmée, appuyez à nouveau sur Confirmer pour envoyer les caractères au PC.

Code source et commentaire

Le code source complet se trouve sur GitHub. Il y a 2 fichiers côté iOS et 1 fichier côté PC.

côté iOS

text_client.py

# text_client.py
import socket

class Sender():
    def __init__(self, port, ipaddr, timeout=5.0):
        self.port = port
        self.ipaddr = ipaddr
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.settimeout(timeout)
    
    def connect(self):
        self.sock.connect((self.ipaddr, self.port))            
    
    def send(self, text, enc="utf-8"):
        """
Envoyer des données
        
        Parameters
        ----------
        text : str
Texte à envoyer
        enc : str
Le type d'encodage. Aucun sans encodage
        """
        if(enc is not None):
            text_ = text.encode(enc)
        else:
            text_ = text
        self.sock.sendall(text_)
    
    def close(self):
        self.sock.close()
 

Classe de transmission de texte. Envoyez des données en utilisant la communication par socket. Nous utiliserons ceci dans le deuxième fichier.

flick_kb.py

# flick_kb.py
import sys
import socket
import pyto_ui as ui
import text_client as myclient

class MySender(myclient.Sender):
    def __init__(self, port, ipaddr, timeout=5.0):
        print(port, ipaddr)
        super().__init__(port, ipaddr, timeout)
        self.editflag = False
    
    def connect(self):
        try:
            super().connect()
            self.editflag = True
            print("connection")
        except socket.timeout:
            print("timed out")
            super().close()
            sys.exit()
    
    def send_text(self, text):
        self.send(text)
    
    def send_end_flag(self):
        #Envoyer un drapeau pour notifier au PC la fin de l'application
        if(self.editflag):
            self.send(b"\x00", enc=None)
            self.editflag = False
            
    def close(self):
        self.send_end_flag()
        super().close()
    
    def did_end_editing(self, sender):
        if(self.editflag):
            if sender.text == " ":
                self.send(b"\x0a", enc=None)  # enter
                sender.text = " "
            elif sender.text == "":
                self.send(b"\x08", enc=None)  # backspace
            else:
                self.send_text(sender.text[1:])  #Envoyer du texte sans l'espace précédent
                sender.text = " "
    
        sender.superview["text_field1"].become_first_responder()  #Gardez la zone de texte floue
    
    def did_change_text(self, sender):
        if sender.text == "":
            sender.text = " "  #Pour la détection de retour arrière
            self.send(b"\x08", enc=None)  # backspace       

def main():
    args = sys.argv
    if(len(args)<=2):
        print("Input arguments. [PORT] [IP Address]")
        sys.exit()
    else:
        port = int(args[1])
        ipaddr = args[2]
    
    #Émetteur
    print("start connection...")
    mysender = MySender(port=port, ipaddr=ipaddr, timeout=5.0)
    mysender.connect()
    
    #Partie GUI
    view = ui.View()
    view.background_color = ui.COLOR_SYSTEM_BACKGROUND
    
    text_field = ui.TextField(placeholder="Back Space")
    text_field.name = "text_field1"
    text_field.text = " "
    text_field.become_first_responder()
    text_field.action = mysender.did_change_text
    text_field.did_end_editing = mysender.did_end_editing
    text_field.return_key_type = ui.RETURN_KEY_TYPE_DONE
    text_field.width = 300
    text_field.center = (view.width/2, view.height/2)
    text_field.flex = [
        ui.FLEXIBLE_BOTTOM_MARGIN,
        ui.FLEXIBLE_TOP_MARGIN,
        ui.FLEXIBLE_LEFT_MARGIN,
        ui.FLEXIBLE_RIGHT_MARGIN
    ]
    view.add_subview(text_field)
    
    ui.show_view(view, ui.PRESENTATION_MODE_SHEET)
    
    #Terminer le traitement
    mysender.close()
    print("end")

if __name__=="__main__":
    main()

C'est un corps de clavier flick. J'utilise pyto_ui.TextField de pyto. La partie GUI est presque la même que l'exemple de code de Pyto.

C'est TextField, mais lorsque vous appuyez sur Entrée, il perd automatiquement le focus. Puisqu'une entrée continue n'est pas possible avec cela, la méthode d'appel de la fonction devenir_premier_répondeur () et de l'afficher à nouveau est prise. Par conséquent, le clavier disparaît un instant après avoir appuyé sur la touche de confirmation.

De plus, comme la méthode mysender.did_change_text définie dans text_field.action de la partie GUI est appelée avant même que les caractères japonais ne soient confirmés, il semble qu'elle ne puisse pas être envoyée au moment où la conversion est confirmée. .. Par conséquent, le texte est envoyé en appuyant deux fois sur le bouton de confirmation, "Confirmer la conversion → Confirmer". Vous devez appuyer trois fois sur la touche de confirmation pour insérer un saut de ligne.

Backspace ne répond pas en l'absence de texte. Par conséquent, j'ai essayé de mettre un espace dans la zone de texte à l'avance. Il est déterminé que Backspace a été pressé lorsque cet espace a été effacé. Une fois effacé, un nouvel espace est immédiatement inséré par le processus sender.text =" " `.

J'ai utilisé UTF-8 LF (0x0a), Backspace (0x08) et NULL (0x00) pour Enter, Backspace et End code, respectivement, mais ils ne sont utilisés que comme indicateurs, ils n'ont donc pas de signification originale. Par conséquent, le code de saut de ligne reflète les paramètres du système d'exploitation et de l'éditeur du PC.

Côté PC

flick_kb_receiver.py

# flick_kb_receiver.py
import sys
import time
import socket
import pyautogui
import pyperclip
import threading

def type_text(text):
    #Entrez les caractères donnés (copiez et collez dans le presse-papiers)
    pyperclip.copy(text)
    pyautogui.hotkey("ctrl", "v")
    return True

def type_backspace():
    pyautogui.typewrite(["backspace"])
    return True

def type_enter():
    pyautogui.typewrite(["enter"])
    return True

class Receiver():
    def __init__(self, port, ipaddr=None, set_daemon=True):
        """
Receveur

        Parameters
        ----------
        port : int
Numéro de port à utiliser
        ipaddr : None or str
Adresse IP du PC récepteur. Acquis automatiquement avec Aucun.
        set_daemon : bool
Voulez-vous faire du thread un démon? Arrêtez le thread principal sans attendre la fin du thread récepteur.
        """
        if(ipaddr is None):
            host = socket.gethostname()
            ipaddr = socket.gethostbyname(host)
        self.ipaddr = ipaddr
        self.port = port
        self.set_daemon = set_daemon
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.loopflag = False
        print("ip:{0} port:{1}".format(self.ipaddr, self.port))
    
    def loop(self):
        self.sock.settimeout(0.5)
        self.sock.bind((self.ipaddr, self.port))
        self.sock.listen(1)
        print("start listening...")
        while(self.loopflag):
            try:
                conn, addr = self.sock.accept()
            except socket.timeout:
                continue
            print("accepted")
            with conn:
                while(self.loopflag):
                    # print("waiting...")
                    data = conn.recv(1024)
                    if(not data):
                        break
                    if(data[:1]==b"\x08"):  #Si vous appuyez successivement sur retour arrière, plusieurs messages seront envoyés en même temps (exemple: b)"\x08\x08\x08), vérifiez donc uniquement les 8 premiers octets
                        print("> BS")
                        type_backspace()
                    elif(data==b"\x0a"):
                        print("> Enter")
                        type_enter()
                    elif(data==b"\x00"):
                        print("STOP CLIENT")
                        break
                    else:
                        text = data.decode("utf-8")
                        print(">", text)
                        type_text(text)

    def start_loop(self):
        self.loopflag = True
        self.thread = threading.Thread(target=self.loop)
        if(self.set_daemon):
            self.thread.setDaemon(True)
        self.thread.start()
        print("start_thread")
    
    def stop_loop(self):
        print("stop loop")
        self.loopflag = False
        time.sleep(0.6)  #Attendez que le socket expire
        if(not self.set_daemon):
            print("waiting to stop client...")  #Attendez que l'expéditeur s'arrête
            self.thread.join()
        print("stop_thread")
    
    def close_sock(self):
        self.sock.close()
        print("socket closed")

def main():
    #Arguments de ligne de commande
    ipaddr = None
    args = sys.argv
    if(len(args)<=1):
        print("Usage: flick_kb_receiver [PORT] [IP (optional)]")
        sys.exit()
    elif(len(args)==2):
        port = int(args[1])
    else:
        port = int(args[1])
        ipaddr = args[2]

    #Traitement principal
    receiver = Receiver(port=port, ipaddr=ipaddr)
    receiver.start_loop()
    while True:
        stopper = input()
        if(stopper=="s"):
            receiver.stop_loop()
            break
    receiver.close_sock()

if __name__=="__main__":
    main()

C'est du côté PC. J'ai utilisé pyautogui pour la saisie de caractères. pyautogui est une bibliothèque qui permet des opérations générales sur PC telles que les opérations de touches et les opérations de souris. Si les données binaires reçues ne sont pas Enter, Backspace ou End code, elles sont décodées et envoyées au processus de saisie de texte.

Cependant, il semble que la saisie japonaise ne soit pas possible avec pyautogui, donc la méthode consiste à enregistrer le texte dans le presse-papiers avec la méthode type_text puis à le coller (Reference. / 2017/10/18/231 750)).

En ce qui concerne le retour arrière, si vous supprimez des caractères en continu du côté du smartphone, plusieurs codes de retour arrière \ x08 peuvent être envoyés en même temps comme \ x08 \ x08 \ x08. Par conséquent, j'écris ʻif (data [: 1] == b "\ x08"): `pour ne voir que le premier octet. D'ailleurs, s'il s'agit de «data [0]», «8» (valeur convertie en nombre décimal) sera renvoyé.

La partie communication s'exécute dans un thread séparé et le thread principal attend la commande de fin de programme. Pendant l'exécution, vous pouvez quitter le programme en tapant s dans la console et en appuyant sur Entrée. Cependant, étant donné que le thread de la section de communication ne s'arrête pas lorsque la communication est connectée, la section de communication est transformée en démon et arrêtée de force lorsque le thread principal se termine. Je ne sais pas si c'est un bon moyen.

mouvement

Placement des fichiers

[PC]

flick_kb_receiver.py

[iOS]

flick_clavier (dossier arbitraire)
├── flick_kb.py (fichier d'exécution)
├── text_client.py

Procédure d'exécution

Connexion / entrée

  1. Exécutez le numéro de port comme argument de ligne de commande côté PC Exemple: python flick_kb_receiver.py 50001
  2. Ouvrez flick_kb.py dans l'application Pyto sur le côté iOS et appuyez sur la marque d'engrenage en bas à droite. Entrez le numéro de port et l'adresse IP dans Arguments, puis exécutez Exemple: 50001 192.168.1.2
  3. Placez le curseur sur n'importe quel éditeur de texte ou fenêtre de recherche sur votre PC.
  4. Après avoir confirmé l'entrée dans la zone de texte du côté iOS, appuyez à nouveau sur Confirmer.
  5. Le texte est saisi côté PC
12C402D0-A9C4-41A1-9B3A-AF6F0A083CF8.jpeg Écran pour définir les arguments dans Pyto

Déconnecter / terminer

  1. Arrêtez le programme du côté iOS
  2. Tapez "s" sur la console côté PC et appuyez sur Entrée pour arrêter le programme côté PC.

Si une erreur se produit dans la section d'importation du côté iOS, cela signifie que l'accès au dossier n'est pas autorisé, alors autorisez-le à partir de la marque de verrouillage Nanjing en bas à droite de l'écran de l'éditeur Pyto.

L'autorisation d'accès au pare-feu a été donnée lorsque le PC a été démarré pour la première fois, mais il ne pouvait pas être connecté à moins que l'accès public ne soit autorisé.

État de fonctionnement

E8D546BF-13AA-411C-8FE0-097E427CB509.gif

Le GIF ci-dessus est une combinaison ultérieure des écrans PC et iPhone. De la connexion à la saisie de caractères dans VSCode et à la déconnexion. Presque aucun retard n'a été ressenti.

en conclusion

Je pense que l'entrée flick est une très bonne technique en tant que clavier pour saisir le japonais avec un petit écran tactile. Cette fois, j'ai en fait essayé la saisie par effleurement sur un PC, et j'ai senti que le seuil pour PC serait abaissé en augmentant les options de saisie par effleurement pour les personnes qui ne peuvent pas utiliser un clavier de PC. Il y avait peu de mouvement de la ligne de visée entre le PC et le smartphone, et j'ai pu entrer sans aucune gêne.

Cependant, je peux frapper plus rapidement avec un clavier PC, donc il n'y a que peu d'avantages à utiliser la saisie par effleurement. Est-il possible de le frapper d'une main ou de l'utiliser en dormant? Après tout, l'entrée clavier du PC peut utiliser tous les doigts et les symboles sont faciles à taper, il est donc bon de l'utiliser.

Recommended Posts

[Pyto] J'ai essayé d'utiliser un smartphone comme clavier pour PC
J'ai essayé d'expliquer à quoi sert le générateur Python aussi facilement que possible.
J'ai essayé de créer un bot pour annoncer un événement Wiire
J'ai essayé de faire une étrange citation pour Jojo avec LSTM
J'ai essayé de créer un linebot (préparation)
J'ai créé une API Web
J'ai créé un conteneur Docker pour utiliser JUMAN ++, KNP, python (pour pyKNP).
J'ai essayé de faire un diagnostic de visage AI pour les golfeuses professionnelles ①
J'ai essayé de faire un diagnostic de visage AI pour les golfeuses professionnelles ②
Je pense que c'est une perte de ne pas utiliser le profileur pour le réglage des performances
J'ai essayé de créer une méthode de super résolution / ESPCN
J'ai essayé d'utiliser lightGBM, xg boost avec Boruta
J'ai essayé de créer une méthode de super résolution / SRCNN ①
J'ai essayé de faire de l'IA pour Smash Bra
J'ai essayé de générer une chaîne de caractères aléatoire
J'ai essayé de créer une méthode de super résolution / SRCNN ③
J'ai essayé de créer un bouton pour Slack avec Raspeye + Tact Switch
J'ai essayé de créer une méthode de super résolution / SRCNN ②
[Python] J'ai essayé d'obtenir le nom du type sous forme de chaîne de caractères à partir de la fonction type
J'ai créé un jeu ○ ✕ avec TensorFlow
[Python] Je souhaite utiliser uniquement l'index lors de la mise en boucle d'une liste avec une instruction for
J'ai essayé de créer un environnement d'apprentissage amélioré pour Othello avec Open AI gym
Quand j'ai recherché "en tant que service" d'AaaS à ZaaS, j'ai vu divers services.
Créer un ensemble de données d'images à utiliser pour la formation
J'ai essayé de faire un "putain de gros convertisseur de littérature"
J'ai essayé de résumer comment utiliser matplotlib de python
J'ai essayé d'implémenter un pseudo pachislot en Python
[Go + Gin] J'ai essayé de créer un environnement Docker
Pratique pour utiliser les sous-graphiques matplotlib dans l'instruction for
J'ai essayé de résumer comment utiliser les pandas de python
J'ai essayé un RPA simple pour me connecter avec du sélénium
J'ai essayé de dessiner un diagramme de configuration à l'aide de diagrammes
J'ai essayé de déboguer.
J'ai essayé d'utiliser une bibliothèque (fil conducteur) qui rend le package de threads Python plus facile à utiliser
[Git] J'ai essayé de faciliter la compréhension de l'utilisation de git stash en utilisant un exemple concret.
[Pour ceux qui veulent utiliser TPU] J'ai essayé d'utiliser l'API de détection d'objets Tensorflow 2
J'ai essayé d'implémenter une ligne moyenne mobile de volume avec Quantx
J'ai essayé de mettre en œuvre le modèle de base du réseau neuronal récurrent
Comment utiliser Fujifilm X-T3 comme webcam sur Ubuntu 20.04
J'ai essayé de porter le code écrit pour TensorFlow sur Theano
J'ai essayé de créer automatiquement un rapport avec la chaîne de Markov
[Chaîne de Markov] J'ai essayé de lire les citations en Python.
J'ai essayé d'utiliser Tensorboard, un outil de visualisation pour l'apprentissage automatique
J'ai essayé de résoudre le problème d'optimisation des combinaisons avec Qiskit
J'ai essayé "Comment obtenir une méthode décorée en Python"
Comment utiliser cuML SVC comme classificateur CV Gridsearch
Je souhaite utiliser un environnement virtuel avec jupyter notebook!
Je ne savais pas comment utiliser l'instruction [python] for
J'ai essayé d'automatiser [une certaine tâche] à l'aide d'une tarte à la râpe
J'ai trébuché lorsque j'ai essayé d'installer Basemap, donc un mémorandum
J'ai essayé de trier une colonne FizzBuzz aléatoire avec un tri à bulles.
J'ai fait un chronomètre en utilisant tkinter avec python
Comment utiliser un fichier autre que .fabricrc comme fichier de configuration
J'ai essayé d'écrire dans un modèle de langage profondément appris
J'ai créé un éditeur de texte simple en utilisant PyQt
Utilisez un langage de script pour une vie C ++ confortable-OpenCV-Port Python vers C ++ -