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)
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 ...)
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").
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()
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