Ich habe einen IPython Notebook-ähnlichen Tkinter-Wrapper geschrieben [Python]

Wie der Titel schon sagt, habe ich einen Wrapper für Tkinter geschrieben, der die GUI wie das interaktive Widget von IPython Notebook einfach aufrufen kann, also werde ich ihn aufgeben. Ich mache mir Sorgen, weil es sich etwas unerwartet verhält, deshalb möchte ich Sie um Ihren Unterricht bitten. Abgesehen davon denke ich, dass es viel einfacher zu bedienen ist, also denke ich darüber nach, es das nächste Mal auf Qt oder Gtk zu portieren.

Nachtrag: Portiert auf Gtk (Link)

out-2_65-139.gif

Motivation zur Schöpfung

Ich habe in diesen Tagen häufig IPython-Notebooks verwendet, aber wenn ich darüber nachdenke, welche Funktionen mich interessieren, kann ich Variablen interaktiv ändern und Funktionen wie Interaktiv und Interaktiv ausführen. Ich denke, es hängt weitgehend von der Existenz ab. Der Rest erstellt Folien. Ich habe meinen eigenen Wrapper in Tkinter geschrieben, war aber beeindruckt von der Benutzerfreundlichkeit des interaktiven Widgets in IPython Notebook. Daher habe ich beschlossen, etwas zu erstellen, das das Tk-Widget auf ähnliche Weise anzeigen kann. .. Daher, wie man verwendet

Es ist fast das gleiche wie das in. Aufgrund der Benutzerfreundlichkeit des Tkinter-Moduls wurde die Dropdown-Liste in ein Optionsfeld geändert. Einige Leute erstellen jedoch Dropdowns mit Tk,

Es scheint, dass es nichts gibt, was Sie nicht tun können, wenn Sie etwas einbauen, aber diesmal habe ich es aufgegeben. (Ich werde Gtk als Haupt verwenden ...)

wie benutzt man

Der aufzurufende Typ ist der gleiche wie [Interact verwenden] Interact, wie bereits erwähnt. Es ist als interaktive Klasse gruppiert. Wenn es aus einem Skript aufgerufen wird, muss w.display () für die Anzeige ausgeführt werden.


>>> 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}

Außerdem tritt in der ursprünglichen Startseite beim Angeben von Argumenten mit interaktiv ein Fehler auf, wenn nicht dieselben Argumente wie die Argumente der zu verknüpfenden Funktion f enthalten sind. Sie müssen eine feste Klasse wie c = fixed (10) für die Argumente angeben, die Sie nicht ändern möchten. Da es diesmal schwierig war, sie zu implementieren, und ich nicht viel Gelegenheit hatte, sie zu verwenden, kann sie mit diesem Wert ausgeführt werden, wenn der Standardwert im Argument der Funktion f festgelegt ist, die mit einer Variablen verknüpft ist, die nicht durch Interactive angegeben wurde Ja (wenn Sie es nicht angeben, wird natürlich ein Tippfehler mit der Meldung "Es gibt nicht genügend Argumente" angezeigt).

Ganzes Skript

Es ist lang (obwohl es viel einfacher als das Original ist), aber bitte schauen Sie, wenn Sie möchten. Es wurde bestätigt, dass es mit Python 2.7.6 funktioniert. Bitte versuchen Sie es. Wenn Sie in Python3.4.0 "Tkinter" in "tkinter" ändern, machen Sie die print-Anweisung zu einer Funktionsnotation und nehmen Sie den Wörterbuchwert im 3. System heraus

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

Weil es so wird

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

Ich konnte es einfach ausführen, indem ich den Teil von arg.value () (Zeilen 97, 116) durch list (arg.value ()) ersetzte, wie in gezeigt.

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

Probleme und Zukunftsaussichten

実行した様子

Das Problem ist, dass, wie Sie sehen können, die Reihenfolge, in der die Variablen angezeigt werden, nicht in der richtigen Reihenfolge ist. Ich denke, das liegt an der Tatsache, dass im Wörterbuchformat darauf verwiesen wird, aber ich weiß nicht, wie ich es lösen soll, also lasse ich es vorerst so, wie es ist. Hier möchte ich etwas dagegen tun. Vielleicht können Sie Ordered Dict verwenden.

Wie Sie aus dem GIF-Bild und dem obigen Bild sehen können, wurde der Inhalt der Funktion f mit der Anzahl der Argumente ausgeführt, die zu Beginn in der Skalierungsleiste angegeben wurden. Möglicherweise ist dies die Skalenspezifikation von Tkinter, und der Wert wird in der Phase der Anzeige festgelegt. Selbst wenn ich scale_change in der Zeile unmittelbar vor self.root.mainloop () der Funktionsanzeige aktiviert habe, hat es nicht funktioniert. Wenn beim Portieren auf Gtk dasselbe Problem auftritt, kann die Ursache unterschiedlich sein. Daher sollte die referenzierte Funktion, wie beim IPython Notebook, eine leichte Funktion (oder eine vollständig Dummy-Funktion) sein, und ich bin der Meinung, dass es stabiler ist, schwere Berechnungen durchzuführen, wenn die Taste gedrückt wird.

Die Zukunftsaussichten möchte ich vorerst auf Gtk ausweiten. In diesem Fall möchte ich Dropdowns, Registerkarten und Fortschrittsbalken einbinden.

Recommended Posts

Ich habe einen IPython Notebook-ähnlichen Tkinter-Wrapper geschrieben [Python]
Ich habe einen IPython Notebook-ähnlichen Gtk-Wrapper geschrieben [Python]
Ich habe ein Skript zur automatischen Erstellung eines leeren Verzeichnisses in Python geschrieben
Ich habe eine SMS mit Python gesendet
Ich habe Fizz Buzz in Python geschrieben
Ich habe die Warteschlange in Python geschrieben
Ich habe den Stack in Python geschrieben
[Python-Anfänger] Ich habe die Artikel gesammelt, die ich geschrieben habe
Ein Memo, das ich schnell in Python geschrieben habe
Ich habe eine Klasse in Python3 und Java geschrieben
Geschrieben "Einführung in die Effektüberprüfung" in Python
[Python] Ich habe mit Tkinter einen Youtube Downloader erstellt.
Ich habe versucht, eine E-Mail mit SendGrid + Python zu senden
Python Qiita API Wrapper "qiipy" gemacht
Ich habe Python gestartet
Ich habe matplotlib geschrieben
[Python] Tkinter-Vorlage
Ich habe eine Fehlermeldung erhalten, dass Python die Datei settings.ini nicht lesen konnte
Grundlagen des Eingabe- / Ausgabebildschirms mit tkinter in python3
Ich habe ein automatisches Kitskript für OpenBlocks IoT geschrieben
Ich erhalte einen Importfehler mit Python Beautiful Soup
Ich habe versucht, künstliches Perzeptron mit Python zu implementieren
Ich habe ein automatisches Installationsskript für Arch Linux geschrieben
Python: Ich habe es mit Lügner und Ehrlichkeit versucht
[Python] Ich habe versucht, mit tkinter eine Anwendung zu erstellen, die das Gehalt anhand der Arbeitszeit berechnet
[Basic Information Engineer Examination] Ich habe einen Algorithmus zur Bestimmung des Jahres der Schwellung in Python geschrieben.