J'ai écrit un wrapper Tkinter semblable à un notebook IPython [Python]

Comme le titre l'indique, j'ai écrit un wrapper pour Tkinter qui peut facilement appeler l'interface graphique comme le widget interactif d'IPython Notebook, donc je vais y renoncer. Je suis inquiet parce que cela se comporte de manière un peu inattendue, alors j'aimerais vous demander votre enseignement. En dehors de cela, je pense que c'est beaucoup plus facile à utiliser, donc je pense à le porter sur Qt ou Gtk la prochaine fois.

Postscript: Porté sur Gtk (lien)

out-2_65-139.gif

Motivation pour la création

J'utilise beaucoup les blocs-notes IPython ces jours-ci, mais quand je pense aux fonctionnalités qui m'attirent, je peux modifier les variables de manière interactive et exécuter des fonctions, telles que l'interaction et l'interactivité. Je pense que cela dépend en grande partie de l'existence. Le reste crée des diapositives. J'ai écrit mon propre wrapper dans Tkinter, mais j'ai été impressionné par la facilité d'utilisation du widget interactif dans IPython Notebook, j'ai donc décidé de créer quelque chose qui pourrait afficher le widget Tk de la même manière. .. Par conséquent, comment utiliser

C'est presque le même que celui répertorié dans. En raison de la commodité du module Tkinter, la liste déroulante a été remplacée par un bouton radio. Cependant, certaines personnes créent des listes déroulantes avec Tk,

Il semble que vous ne puissiez rien faire si vous incorporez quelque chose, mais cette fois j'ai abandonné. (Je vais utiliser Gtk comme principal ...)

comment utiliser

Le type à appeler est le même que [Utilisation d'Interact] [interagir] comme mentionné précédemment. Il est regroupé en tant que classe interactive, et lorsqu'il est appelé à partir d'un script, il est nécessaire d'exécuter w.display () pour l'affichage.


>>> from tk_wrapper import interactive
>>> def f(a,b=20):
...     return a + b
...
>>> w = interactive(f, a=(0, 20, 2), b=20)

(in console, tk widget appear here)
(you can add buttons and label here manually)

(then, you should add the next line)
>>> w.display()

(and you can get the result for f(a,b) by)
>>> w.result
32

(or get the arguments with a dictionary)
>>> w.kwargs
{'a':8, 'b':24}

De plus, dans le home d'origine, lors de la spécification d'arguments avec interactive, une erreur se produira si tous les mêmes arguments que les arguments de la fonction f à lier ne sont pas inclus. Vous devez spécifier une classe fixe telle que c = fixed (10) pour les arguments que vous ne souhaitez pas modifier. Comme c'était difficile à implémenter cette fois et que je n'avais pas beaucoup l'occasion de l'utiliser, si la valeur par défaut est définie dans l'argument de la fonction f liée à la variable non spécifiée par interactive, elle peut être exécutée en utilisant cette valeur telle quelle Oui (bien sûr, si vous ne le spécifiez pas, vous obtiendrez une erreur de type disant "Il n'y a pas assez d'arguments").

Script entier

C'est long (bien que ce soit beaucoup plus simple que l'original), mais jetez un œil si vous le souhaitez. Il a été confirmé qu'il fonctionne avec Python 2.7.6, veuillez donc l'essayer. Même en Python3.4.0, si vous changez "Tkinter" en "tkinter", faites de l'instruction print une notation de fonction, puis retirez la valeur du dictionnaire dans le 3e système

>>> a = {"d": 1, "c":4, "b":5}
>>> a.values()
dict_values([4, 5, 1])

Parce que ça devient comme

>>> list(a.values())
[4, 5, 1]

J'ai pu l'exécuter simplement en remplaçant la partie de arg.value () (lignes 97,116) par list (arg.value ()) comme indiqué dans.

tk_wrapper.py


#! /usr/bin/env python
# -*- coding:utf-8 -*-
#
# written by ssh0, October 2014.
__doc__ = '''IPython-like Tkinter wrapper class.

You can create Scalebar, Checkbutton, Radiobutton via simple interface.

usage example:

>>> from tk_wrapper import interactive
>>> def f(a,b=20):
...     return a + b
...
>>> w = interactive(f, a=(0, 20, 2), b=20)

(in console, tk widget appear here)
(you can add buttons and label here manually)

(then, you should add the next line)
>>> w.display()

(and you can get the result for f(a,b) by)
>>> w.result
32

(or get the arguments with a dictionary)
>>> w.kwargs
{'a':8, 'b':24}
'''

import Tkinter
import inspect


class interactive(object):

    def __init__(self, func, title='title', **kwargs):
        self.__doc__ = __doc__
        self.func = func
        self.kwargs = dict()
        args, varargs, keywords, defaults = inspect.getargspec(self.func)
        d = []
        for default in defaults:
            d.append(default)
        self.kwdefaults = dict(zip(args[len(args)-len(defaults):], d))

        self.root = Tkinter.Tk()
        self.root.title(title)
        self.f = Tkinter.Frame(self.root)
        for kw, arg in kwargs.items():
            kw = str(kw)
            arg_type = type(arg)
            if arg_type == tuple:
                # type check for elements in tuple
                argtype = self.type_check(arg)
                if argtype == str:
                    self.radiobuttons_str(kw, arg)
                else:
                    self.scale_bar(kw, arg, argtype)
            elif arg_type == int or arg_type == float:
                self.scale_bar(kw, [arg], arg_type)
            elif arg_type == bool:
                self.checkbutton(kw, arg)
            elif arg_type == str:
                self.label(arg)
                self.kwargs[kw] = arg
            elif arg_type == dict:
                self.radiobuttons_dict(kw, arg)
            else:
                raise TypeError

        self.f.pack()
        self.status = Tkinter.Label(self.root, text=str(self.kwargs))
        self.status.pack()

    def display(self):
        self.root.mainloop()

    def status_change(self):
        self.status.config(text=str(self.kwargs))
        self.kwargs_for_function = self.kwdefaults
        for k, v in self.kwargs.items():
            self.kwargs_for_function[k] = v
        self.result = self.func(**self.kwargs_for_function)

    def set_value(self, var, kw, new_value):
        # if argument is already given in func, use it for a default one
        if kw in self.kwdefaults:
            var.set(self.kwdefaults[kw])
            self.kwargs[kw] = self.kwdefaults[kw]
        else:
            var.set(new_value)
            self.kwargs[kw] = new_value

    def type_check(self, arg):
        argtype = type(arg[0])
        if not all([type(a) == argtype for a in arg]):
            raise TypeError("""types in a tuple must be the same.
            int or float: Scalebar
            str         : Radiobuttons""")
        return argtype

    def scale_bar(self, kw, arg, argtype):

        def scale_interact(new):
            self.kwargs[kw] = var.get()
            self.status_change()

        # length check for tuple
        len_arg = len(arg)
        if len_arg > 3 or len_arg == 0:
            raise IndexError("tuple must be 1 or 2 or 3 element(s)")

        if argtype == int:
            var = Tkinter.IntVar()
            scale_resolution = 1
        elif argtype == float:
            var = Tkinter.DoubleVar()
            scale_resolution = 0.1
        else:
            raise TypeError("arg must be int or float")

        # set the values
        if len_arg == 3:
            scale_from = arg[0]
            scale_to = arg[1]
            scale_resolution = arg[2]
        elif len_arg == 2:
            scale_from = arg[0]
            scale_to = arg[1]
        else:
            scale_from = arg[0]*(-1)
            scale_to = arg[0]*2

        self.set_value(var, kw, arg[0])
        # create scale widget in frame
        scale = Tkinter.Scale(self.f, label=kw, from_=scale_from, to=scale_to,
                              resolution=scale_resolution, variable=var,
                              orient='h', width=10, length=200,
                              command=scale_interact)
        scale.pack()

    def checkbutton(self, kw, arg):

        def check_interact():
            self.kwargs[kw] = var.get()
            self.status_change()

        var = Tkinter.BooleanVar()
        self.set_value(var, kw, arg)
        checkbutton = Tkinter.Checkbutton(self.f, text=kw, variable=var,
                                          command=check_interact)
        checkbutton.pack()

    def radiobuttons_str(self, kw, arg):

        def select():
            self.kwargs[kw] = var.get()
            self.status_change()

        var = Tkinter.StringVar()
        label = Tkinter.Label(self.f, text='select one for '+kw)
        label.pack()
        self.set_value(var, kw, arg[0])
        for x in arg:
            R = Tkinter.Radiobutton(self.f, text=x, variable=var,
                                    value=x, command=select)
            R.pack()

    def radiobuttons_dict(self, kw, arg):

        def select():
            self.kwargs[kw] = var.get()
            self.status_change()

        vtype = self.type_check(arg.values())
        if vtype == str:
            var = Tkinter.StringVar()
        elif vtype == int:
            var = Tkinter.IntVar()
        elif vtype == float:
            var = Tkinter.DoubleVar()
        else:
            raise TypeError("arg must be int or float or str.")
        label = Tkinter.Label(self.f, text='select one for '+kw)
        label.pack()
        self.set_value(var, kw, arg.values()[0])
        for k, d in arg.items():
        
            R = Tkinter.Radiobutton(self.f, text=k, variable=var,
                                    value=d, command=select)
            R.pack()

if __name__ == '__main__':
    import sys
    import Tkinter

    def f(x, y, z, o=True, i=0):
        print x, y, z, o, i

    def b1():
        print w.kwargs

    buttons = [('b1', b1), ('exit', sys.exit)]
    w = interactive(f, x=(0, 10), y=(1, 100, 10),
                    z=("ZZZ", "III", "foo", "bar"),
                    i={'0':0, '10':10, '20':20},
                    o=True
                    )

    for button in buttons:
        button = Tkinter.Button(w.root, text=button[0], command=button[1])
        button.pack()

    w.display()

Problèmes et perspectives d'avenir

実行した様子

Le problème est que, comme vous pouvez le voir, l'ordre dans lequel les variables sont affichées est dans le désordre. Je pense que cela est dû au fait qu'il est référencé au format dictionnaire, mais je ne sais pas comment le résoudre, donc je le laisse tel quel pour le moment. C'est là que je veux faire quelque chose. Peut-être que vous pouvez utiliser Ordered Dict.

Ensuite, comme vous pouvez le voir sur l'image gif et l'image ci-dessus, le contenu de la fonction f a été exécuté par le nombre d'arguments spécifié dans la barre d'échelle au début. C'est peut-être la spécification de l'échelle de Tkinter, et la valeur est définie au stade de l'affichage. Même si j'ai activé scale_change sur la ligne immédiatement avant self.root.mainloop () de l'affichage de la fonction, cela n'a pas fonctionné. Si le même problème se produit lors du portage vers Gtk, la cause peut être différente. Par conséquent, comme avec le notebook IPython, la fonction référencée devrait être une fonction légère (ou une fonction complètement factice), et je pense qu'il est plus stable d'effectuer des calculs lourds lorsque le bouton est enfoncé.

En ce qui concerne les perspectives d'avenir, je voudrais l'étendre à Gtk pour le moment, et dans ce cas, je voudrais incorporer des listes déroulantes, des onglets et des barres de progression.

Recommended Posts

J'ai écrit un wrapper Tkinter semblable à un notebook IPython [Python]
J'ai écrit un wrapper Gtk semblable à un notebook IPython [Python]
J'ai écrit un script de création automatique de répertoire vide en Python
J'ai envoyé un SMS avec Python
J'ai écrit Fizz Buzz en Python
J'ai écrit la file d'attente en Python
J'ai écrit la pile en Python
[Python débutant] J'ai rassemblé les articles que j'ai écrits
Un mémo que j'ai écrit un tri rapide en Python
J'ai écrit une classe en Python3 et Java
J'ai écrit "Introduction à la vérification des effets" en Python
[Python] J'ai créé un téléchargeur Youtube avec Tkinter.
J'ai essayé d'envoyer un email avec SendGrid + Python
Création du wrapper d'API Qiita Python "qiipy"
J'ai commencé Python
J'ai écrit matplotlib
[Python] Modèle Tkinter
J'ai eu une erreur indiquant que Python n'a pas pu lire settings.ini
Bases de l'écran d'entrée / sortie en utilisant tkinter en python3
J'ai écrit un script de kit automatique pour OpenBlocks IoT
J'obtiens une erreur d'importation avec Python Beautiful Soup
J'ai essayé d'implémenter le perceptron artificiel avec python
J'ai écrit un script d'installation automatique pour Arch Linux
Python: j'ai essayé menteur et honnête
[Python] J'ai essayé de faire une application qui calcule le salaire en fonction des heures de travail avec tkinter
[Examen d'ingénieur d'information de base] J'ai écrit un algorithme pour déterminer l'année de gonflement en Python.