1
votes

Se déplacer entre les zones de saisie des widgets d'entrée à l'aide des touches fléchées haut et bas

from tkinter import *

class Interface_graphique(Frame):

    def __init__(self, root, **kwargs):
        Frame.__init__(self, root, width=768, height=576, **kwargs)
        self.pack(fill=BOTH)

        self.message_1 = Label(self, text="value1")
        self.message_1.grid(row=0, sticky='w')
        self.entry_1 = Entry(self)
        self.entry_1.grid(row=0, column=1)

        self.message_2 = Label(self, text="value2")
        self.message_2.grid(row=1, sticky='w')
        self.entry_2 = Entry(self)
        self.entry_2.grid(row=1, column=1)

root = Tk()
interface = Interface_graphique(root)
interface.mainloop()
I want to type in the Entry widget 'value1' and press the down arrow button to put the cursor in the Entry widget 'value2. Is it possible?

1 commentaires

J'imagine que c'est possible. Vous devez .bind () la touche fléchée pour exécuter les fonctions TAB et Shift + TAB. Probablement avec ctypes


3 Réponses :


-1
votes

Une solution serait d'utiliser une liste pour stocker vos champs de saisie, puis de lier chaque champ avec une référence au champ précédent et suivant de la liste en fonction de l'index. avantage supplémentaire de ne travailler que dans les champs de saisie. Vous n'aurez pas à vous soucier de l'utilisation des touches fléchées lorsqu'un champ de saisie n'a pas du tout le focus sur votre interface graphique.

import tkinter as tk
import ctypes
from ctypes import wintypes
import time

user32 = ctypes.WinDLL('user32', use_last_error=True)

INPUT_MOUSE = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2

KEYEVENTF_EXTENDEDKEY = 0x0001
KEYEVENTF_KEYUP = 0x0002
KEYEVENTF_UNICODE = 0x0004
KEYEVENTF_SCANCODE = 0x0008

MAPVK_VK_TO_VSC = 0

# msdn.microsoft.com/en-us/library/dd375731
VK_TAB = 0x09
VK_SHIFT = 0x10


# C struct definitions

wintypes.ULONG_PTR = wintypes.WPARAM


class MOUSEINPUT(ctypes.Structure):
    _fields_ = (("dx", wintypes.LONG),
                ("dy", wintypes.LONG),
                ("mouseData", wintypes.DWORD),
                ("dwFlags", wintypes.DWORD),
                ("time", wintypes.DWORD),
                ("dwExtraInfo", wintypes.ULONG_PTR))


class KEYBDINPUT(ctypes.Structure):
    _fields_ = (("wVk", wintypes.WORD),
                ("wScan", wintypes.WORD),
                ("dwFlags", wintypes.DWORD),
                ("time", wintypes.DWORD),
                ("dwExtraInfo", wintypes.ULONG_PTR))

    def __init__(self, *args, **kwds):
        super(KEYBDINPUT, self).__init__(*args, **kwds)
        # some programs use the scan code even if KEYEVENTF_SCANCODE
        # isn't set in dwFflags, so attempt to map the correct code.
        if not self.dwFlags & KEYEVENTF_UNICODE:
            self.wScan = user32.MapVirtualKeyExW(self.wVk,
                                                 MAPVK_VK_TO_VSC, 0)


class HARDWAREINPUT(ctypes.Structure):
    _fields_ = (("uMsg", wintypes.DWORD),
                ("wParamL", wintypes.WORD),
                ("wParamH", wintypes.WORD))


class INPUT(ctypes.Structure):
    class _INPUT(ctypes.Union):
        _fields_ = (("ki", KEYBDINPUT),
                    ("mi", MOUSEINPUT),
                    ("hi", HARDWAREINPUT))

    _anonymous_ = ("_input",)
    _fields_ = (("type", wintypes.DWORD),
                ("_input", _INPUT))


LPINPUT = ctypes.POINTER(INPUT)


def _check_count(result, func, args):
    if result == 0:
        raise ctypes.WinError(ctypes.get_last_error())
    return args


user32.SendInput.errcheck = _check_count
user32.SendInput.argtypes = (wintypes.UINT,  # nInputs
                             LPINPUT,  # pInputs
                             ctypes.c_int)  # cbSize


# Functions

def PressKey(hexKeyCode):
    x = INPUT(type=INPUT_KEYBOARD,
              ki=KEYBDINPUT(wVk=hexKeyCode))
    user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))


def ReleaseKey(hexKeyCode):
    x = INPUT(type=INPUT_KEYBOARD,
              ki=KEYBDINPUT(wVk=hexKeyCode,
                            dwFlags=KEYEVENTF_KEYUP))
    user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))


root = tk.Tk()


def up_key(event):
    PressKey(VK_SHIFT)
    PressKey(VK_TAB)
    ReleaseKey(VK_TAB)
    ReleaseKey(VK_SHIFT)


def down_key(event):
    PressKey(VK_TAB)
    ReleaseKey(VK_TAB)


tk.Entry(root).pack()
tk.Entry(root).pack()
tk.Entry(root).pack()
tk.Entry(root).pack()
tk.Entry(root).pack()

root.bind('<Up>', up_key)
root.bind('<Down>', down_key)
root.mainloop()

Comme Bryan l'a souligné ctypes code> est un peu exagéré, mais je voulais laisser cela à tous les curieux de ctypes tout de même.

Vous pouvez utiliser ctypes pour utiliser le clavier et j'ai copié une structure ctypes bien connue et créé une interface graphique tkinter simple pour illustrer comment vous pouvez lier les touches fléchées haut et bas pour fonctionner comme des onglets.

Faites-moi savoir si vous avez des questions.

import tkinter as tk


root = tk.Tk()
entry_list = []


def move_entry(x):
    entry_list[x].focus()


for i in range(5):
    entry_list.append(tk.Entry(root))
    entry_list[-1].pack()

    if i == 0:
        entry_list[-1].bind('<Down>', lambda e, x=i: move_entry(x + 1))
    elif i == 4:
        entry_list[-1].bind('<Up>', lambda e, x=i: move_entry(x - 1))
    else:
        entry_list[-1].bind('<Up>', lambda e, x=i: move_entry(x - 1))
        entry_list[-1].bind('<Down>', lambda e, x=i: move_entry(x + 1))


root.mainloop()


4 commentaires

Ce serait bien de savoir quel est le vote défavorable. Cela fonctionne avec les ctypes.


ctypes est exagéré et ne fonctionne que sur Windows. Ce n'est pas une bonne solution à usage général.


J'étais également à la recherche d'une solution plus simple utilisant une liste pour héberger les champs de saisie. Je mettrai à jour ma réponse avec des options supplémentaires.


@BryanOakley J'ai ajouté une solution plus simple qui n'implique pas de ctypes à ma réponse. J'ai laissé l'autre exemple à tous ceux qui voulaient tout de même y jeter un œil.



1
votes

Vous pouvez utiliser bind et focus :

Tkinter fournit un mécanisme puissant pour vous permettre de gérer les événements toi même. Pour chaque widget, vous pouvez lier des fonctions et des méthodes Python aux événements.

from tkinter import *

class Interface_graphique(Frame):

    def __init__(self, root, **kwargs):
        Frame.__init__(self, root, width=768, height=576, **kwargs)
        self.pack(fill=BOTH)

        self.message_1 = Label(self, text="value1")
        self.message_1.grid(row=0, sticky='w')
        self.entry_1 = Entry(self)
        self.entry_1.grid(row=0, column=1)

        self.message_2 = Label(self, text="value2")
        self.message_2.grid(row=1, sticky='w')
        self.entry_2 = Entry(self)
        self.entry_2.grid(row=1, column=1)

        root.bind('<Down>', self.downKey)
        root.bind('<Up>', self.upKey)

    def downKey(self, event):
        self.entry_2.focus()

    def upKey(self, event):
        self.entry_1.focus()

root = Tk()

interface = Interface_graphique(root)
interface.mainloop()

Si un événement correspondant à la description de l'événement se produit dans le widget, le le gestionnaire donné est appelé avec un objet décrivant l'événement.

.

Dirigez le focus d'entrée vers ce widget.

widget.focus()

Si l'application n'a actuellement pas le focus, ce widget obtenir le focus si l'application obtient le focus via la fenêtre gestionnaire.

widget.bind(event, handler)


0 commentaires

1
votes

En bref, créez une reliure qui déplace le focus où vous le souhaitez. Il existe de nombreuses façons de procéder. Si vous n'avez que deux widgets, vous pouvez coder en dur les widgets. Cependant, tkinter permet de fournir assez facilement une solution générale qui fonctionnera toujours dans chaque widget d'entrée.

Lorsque vous créez une liaison, la fonction liée recevra un objet représentant l'événement. L'un des attributs de cet objet est le widget qui a capturé l'événement. Ce widget a une méthode nommée tk_focusNext qui renverra le widget suivant dans l'ordre de focus standard.

Vous pouvez ajouter une liaison à tous les widgets d'entrée qui utilisent cette fonction pour changer le focus . Dans l'exemple suivant, nous lions la flèche vers le bas à la classe de widget Entry pour appeler une fonction qui déplace le focus

Commencez par créer une méthode qui peut être liée à l'événement :

self.entry_1.bind("<Down>", self.next_widget)
self.entry_2.bind("<Down>", self.next_widget)

Ensuite, utilisez ceci pour vous lier à chaque widget d'entrée:

root.bind_class("Entry", "<Down>", self.next_widget)

ou , liez à chaque widget d'entrée individuel:

    def next_widget(self, event):
        event.widget.tk_focusNext().focus()
        return "break"


4 commentaires

J'essaye de faire la même chose avec un cadre défilé mais ça ne marche pas. def int (racine): sf = ScrolledFrame (racine, True, True); sf.pack (fill = 'both', expand = True); Label (sf.inner, text = "value1"). Grid (row = 0, sticky = 'w'); entry_1 = Entrée (sf.inner) .grid (ligne = 0, colonne = 1); root.bind_class ("Entrée", "", sf.inner.next_widget); frame = sf.inner; def next_widget (cadre, événement): event.widget.tk_focusPrev (). focus (); retourne "pause";


Je suis désolé, j'ai essayé de créer un bloc de pré-code avec ctrl + K, mais je n'ai pas réussi


Pour le cadre défilé, j'ai remplacé d'abord self par sf, puis par sf.inner mais les deux solutions ne fonctionnent pas


@Ing:: "ne fonctionne pas" est une phrase inutile, et la section des commentaires n'est pas l'endroit approprié pour poser une nouvelle question ou publier un nouveau code.