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

L'autre jour

J'ai écrit un wrapper Tkinter comme IPython Notebook [Python]

J'ai écrit ça. Cette fois, comme je l'ai mentionné, je vais présenter la même chose portée sur Gtk. Veuillez vous référer à l'article ci-dessus pour savoir comment appeler. Je ne vois pas vraiment les tutoriels japonais de Gtk, donc j'espère que vous comprenez qu'il est étonnamment facile à utiliser.

gtk_wrapper.png

spécification

Cette fois, il est entièrement compatible avec Python3. J'ai apporté la fonction d'impression de \ _ \ _ future \ _ \ _ au début du code, mais je n'en ai pas besoin lorsque je l'appelle en tant que module. Même dans le test, cela ne fonctionne qu'avec 3 systèmes.

Le dernier changement majeur par rapport au notebook IPython est le troisième argument lors de l'appel de la barre d'échelle. Jusqu'à présent, la taille du pas était définie par vous-même (exemple: x = (0., 10., 0.5) crée une barre d'échelle avec 0.5 pas de 0.0 à 10.0), mais en fonction de Gtk Il y a quelque chose appelé set_digits, et nous l'utilisons. En d'autres termes, cette partie a été modifiée en un format qui spécifie le nombre de chiffres après la virgule décimale à prendre. Bien sûr, cette partie peut être omise comme précédemment, et si une valeur entière est donnée comme premier argument, elle peut être spécifiée comme un entier, et si une fraction est donnée à cela, elle devrait prendre jusqu'à 2 décimales. Il y a. Concernant l'arrondi des valeurs ici, vous pouvez voir que vous pouvez le régler plus finement en lisant la référence, mais je ne l'ai pas falsifié cette fois.

Par conséquent, le contenu de l'argument mot-clé donné à interactive est converti en un widget comme suit.

Keyword argumentWidget
`True` or `False`SwitchWiget
`value` or `(min,max)` or `(min,max,digit)` if integers are passedIntSliderWidget
`value` or `(min,max)` or `(min,max,digit)` if floats are passedFloatSliderWidget
`('orange','apple')` or `{'one':1,'two':2}`ComboBoxWidget

Convivialité

J'ai pris une capture d'écran de la façon dont je l'ai déplacé, donc je vais en présenter quelques-uns. Après tout, il est bien plus beau que Tk. Je suis également heureux que le thème Gtk ait été appliqué.

Lorsqu'il est affiché dans Gtk à l'aide de tk_wrapper,

Selection_014.png

Le widget est généré comme ceci.

Lorsque vous déplacez le curseur ou activez et désactivez le commutateur, le contenu de la fonction est exécuté. (Maintenant, affichez simplement le contenu de la variable) En appuyant sur le bouton, vous pouvez exécuter une autre fonction à ce moment, et vous pouvez vous référer à la valeur de la variable à ce moment.

Selection_015.png

Code entier

Enfin, exposez tout le code. Je suis désolé qu'il y ait encore quelques commentaires, mais j'espère que cela aide.

gist

gtk_wrapper.py



#! /usr/bin/env python
# -*- coding:utf-8 -*-
#
# written by ssh0, October 2014.

from __future__ import print_function

__doc__ = '''IPython-like Gtk wrapper class.

You can create Scalebar, Switch, ComboBox via simple interface.

usage example:

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

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

from gi.repository import Gtk
import inspect


class interactive(Gtk.Window):

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

        Gtk.Window.__init__(self, title=title)
        hbox = Gtk.Box(spacing=6)
        self.add(hbox)

        self.listbox = Gtk.ListBox()
        self.listbox.set_selection_mode(Gtk.SelectionMode.NONE)
        hbox.pack_start(self.listbox, True, True, 0)
        self.status = Gtk.Label()

        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.combobox_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.switch(kw, arg)
            elif arg_type == str:
                self.label(arg)
                self.kwargs[kw] = arg
            elif arg_type == dict:
                self.combobox_dict(kw, arg)
            else:
                raise TypeError

        row = Gtk.ListBoxRow()
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
        row.add(hbox)
        hbox.pack_start(self.status, True, True, 10)
        self.status.set_text(str(self.kwargs))
        self.listbox.add(row)

    def display(self):
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()
        Gtk.main()

    def status_change(self):
        self.status.set_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, kw, new_value):
        # if argument is already given in func, use it for a default one
        if kw in self.kwdefaults:
            self.kwargs[kw] = self.kwdefaults[kw]
            return self.kwdefaults[kw]
        else:
            self.kwargs[kw] = new_value
            return 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         : Combobox""")
        return argtype

    def add_label(self, kw, parent):
        label = Gtk.Label(kw, xalign=0)
        parent.pack_start(label, True, True, 10)

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

        def scale_interact(scale, _type):
            if _type == int:
                self.kwargs[kw] = int(scale.get_value())
            else:
                self.kwargs[kw] = float(scale.get_value())
            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:
            scale_digit = 0
        elif argtype == float:
            scale_digit = 2
        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_digit = arg[2]
        elif len_arg == 2:
            scale_from = arg[0]
            scale_to = arg[1]
        else:
            scale_from = arg[0] * (-1)
            scale_to = arg[0] * 3

        # create scale widget in listbox
        row = Gtk.ListBoxRow()
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
        row.add(hbox)

        self.add_label(kw, hbox)
        scale = Gtk.Scale()
        scale.set_range(scale_from, scale_to)
        scale.set_digits(scale_digit)
        scale.set_value(self.set_value(kw, arg[0]))
        scale.set_draw_value(True)
        scale.connect('value-changed', scale_interact, argtype)
        hbox.pack_start(scale, True, True, 10)

        self.listbox.add(row)

    def switch(self, kw, arg):

        def on_switch_activated(switch, gparam):
            self.kwargs[kw] = switch.get_active()
            self.status_change()

        # create switch widget in listbox
        row = Gtk.ListBoxRow()
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
        row.add(hbox)
        self.add_label(kw, hbox)
        switch = Gtk.Switch()
        switch.connect("notify::active", on_switch_activated)
        switch.set_active(self.set_value(kw, arg))
        hbox.pack_start(switch, False, False, 10)
        self.listbox.add(row)

    def combobox_str(self, kw, arg):

        def on_combo_changed(combo):
            tree_iter = combo.get_active()
            if tree_iter is not None:
                self.kwargs[kw] = arg[tree_iter]
                self.status_change()

        argstore = Gtk.ListStore(str)
        for a in arg:
            argstore.append([a])

        # create combobox widget in listbox
        row = Gtk.ListBoxRow()
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
        row.add(hbox)
        self.add_label(kw, hbox)
        combo = Gtk.ComboBox.new_with_model(argstore)
        combo.connect("changed", on_combo_changed)
        renderer_text = Gtk.CellRendererText()
        combo.pack_start(renderer_text, True)
        combo.add_attribute(renderer_text, "text", 0)
        combo.set_active(arg.index(self.set_value(kw, arg)))
        hbox.pack_start(combo, False, False, True)
        self.listbox.add(row)

    def combobox_dict(self, kw, arg):

        def on_combo_changed(combo):
            tree_iter = combo.get_active()
            if tree_iter is not None:
                self.kwargs[kw] = values[tree_iter]
                self.status_change()

        argstore = Gtk.ListStore(str)
        keys = list(arg.keys())
        values = list(arg.values())
        for a in keys:
            argstore.append([a])

        # create combobox widget in listbox
        row = Gtk.ListBoxRow()
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
        row.add(hbox)

        self.add_label(kw, hbox)
        combo = Gtk.ComboBox.new_with_model(argstore)
        combo.connect("changed", on_combo_changed)
        renderer_text = Gtk.CellRendererText()
        combo.pack_start(renderer_text, True)
        combo.add_attribute(renderer_text, "text", 0)
        combo.set_active(values.index(self.set_value(kw, arg)))
        hbox.pack_start(combo, False, False, True)

        self.listbox.add(row)


if __name__ == '__main__':
    from gi.repository import Gtk

    def f(x=12, y=20, z='III', o=False, i=20):
        print("x: {0}, y: {1}, z: {2}, o: {3}, i: {4}".format(x, y, z, o, i))

    def b1(button):
        print(w.kwargs)

    buttons = [('b1', b1), ('exit', Gtk.main_quit)]
    w = interactive(f, x=10, y=(1., 100.),
                    z=("ZZZ", "III", "foo", "bar"),
                    i={'0': 0, '10': 10, '20': 20},
                    o=True
                    )
    row = Gtk.ListBoxRow()
    hbox = Gtk.HBox(spacing=10)
    row.add(hbox)
    for b in buttons:
        button = Gtk.Button(b[0])
        button.connect('clicked', b[1])
        hbox.pack_start(button, True, True, 0)
    w.listbox.add(row)

    w.display()

Défis et perspectives

Je voulais utiliser Gtk pour l'essentiel, c'est donc un paragraphe. Cependant, la fonction est toujours exécutée autant de fois que le nombre de glissières (c'est-à-dire que ce n'est pas un problème uniquement pour Tk), et l'ordre d'affichage est également dans le désordre. Les points à améliorer à l'avenir sont les points ci-dessus et l'amélioration de la barre de menus. Je pense que vous pouvez facilement lire les fichiers. Qt est en attente car je n'ai pas beaucoup de chance de l'utiliser personnellement (Ubuntu ...).

Recommended Posts

J'ai écrit un wrapper Gtk semblable à un notebook IPython [Python]
J'ai écrit un wrapper Tkinter semblable à un notebook IPython [Python]
J'ai écrit python en japonais
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 "Introduction à la vérification des effets" en Python
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
J'ai eu une erreur indiquant que Python n'a pas pu lire settings.ini
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
Python: j'ai essayé menteur et honnête
[Examen d'ingénieur d'information de base] J'ai écrit un algorithme pour déterminer l'année de gonflement en Python.