Source code for loop_step.tk_loop

# -*- coding: utf-8 -*-

"""The graphical part of a Loop step"""

import logging
import seamm
import loop_step
import tkinter as tk

import seamm_widgets as sw

logger = logging.getLogger(__name__)

search_fields = {
    "system index": {
        "operators": (
            "=",
            "!=",
            "between",
            "not between",
            ">",
            "<",
        ),
    },
    "system name": {
        "operators": (
            "is",
            "is not",
            "contains",
            "does not contain",
            "matches",
            "does not match",
            "matches regexp",
            "does not match regexp",
        ),
    },
    "configuration name": {
        "operators": (
            "is",
            "is not",
            "contains",
            "does not contain",
            "matches",
            "does not match",
            "matches regexp",
            "does not match regexp",
        ),
    },
}


[docs] class TkLoop(seamm.TkNode): """The node_class is the class of the 'real' node that this class is the Tk graphics partner for """ node_class = loop_step.Loop def __init__( self, tk_flowchart=None, node=None, canvas=None, x=120, y=20, w=200, h=50, my_logger=logger, ): """Initialize the graphical Tk Loop node Keyword arguments: """ # Set the logging level for this module if requested # if 'tk_loop_log_level' in self.options: # logger.setLevel(self.options.tk_loop_log_level) # logger.critical( # 'Set log level to {}'.format(self.options.tk_loop_log_level) # ) # Call the constructor for the energy super().__init__( tk_flowchart=tk_flowchart, node=node, node_type="loop", canvas=canvas, x=x, y=y, w=w, h=h, my_logger=my_logger, )
[docs] def create_dialog(self): """Create the dialog!""" frame = super().create_dialog(title="Edit Loop Step") # Create the widgets and grid them in P = self.node.parameters for key in P: if key != "search criteria": self[key] = P[key].widget(frame) # Create the search widgets for the systems self["criteria"] = sw.SearchCriteria( frame, text="Select systems where", labelanchor=tk.NW, inclusiontext="", inclusionvalues=( "", "and", "or", "(", ")", "ignore", ), operatorvalues=( "is", "is not", "contains", "does not contain", "matches", "does not match", "matches regexp", "does not match regexp", ), fieldvalues=[*search_fields.keys()], two_values=("between", "not between"), command=self.criteria_callback, ) for widget in ( "type", "where", "query-op", "where system name", "default configuration", ): self[widget].bind("<<ComboboxSelected>>", self.reset_dialog) self[widget].combobox.config(state="readonly") self["errors"].combobox.config(state="readonly")
[docs] def criteria_callback(self, widget, criterion, event, what): """Handle changes in the search criteria widget. Parameters ---------- widget : Tk widget The widget. criterion : sw.Criterion The row -- Criterion widget -- in the table. event : tk.Event The event causing the callback. what : str The item that changed: 'inclusion', 'field', 'operator', 'self.two_values', 'clear', 'set', 'add row', 'remove row' """ if criterion is not None: inclusion, field, operator, value, value2 = criterion.get() if what == "field": operators = search_fields[field]["operators"] w = criterion.operator w.configure(values=operators) if operator in operators: w.set(operator) else: w.set(operators[0])
[docs] def reset_dialog(self, widget=None): """Lay out the edit dialog according to the type of loop.""" # Get the type of loop currently requested loop_type = self["type"].get() logger.debug("Updating edit loop dialog: {}".format(loop_type)) # Remove any widgets previously packed frame = self["frame"] for slave in frame.grid_slaves(): slave.grid_forget() # keep track of the row in a variable, so that the layout is flexible # if e.g. rows are skipped to control such as 'method' here row = 0 self["type"].grid(row=row, column=0, columnspan=2, sticky=tk.W) if loop_type == "For": self["variable"].grid(row=row, column=2, sticky=tk.W) self["start"].grid(row=row, column=3, sticky=tk.W) self["end"].grid(row=row, column=4, sticky=tk.W) self["step"].grid(row=row, column=5, sticky=tk.W) elif loop_type == "Foreach": frame.columnconfigure(5, weight=0) self["variable"].grid(row=row, column=2, sticky=tk.W) self["values"].grid(row=row, column=3, sticky=tk.EW) frame.columnconfigure(3, weight=1) elif loop_type == "For rows in table": frame.columnconfigure(3, weight=0) self["table"].grid(row=row, column=2, columnspan=2, sticky=tk.EW) row += 1 self["where"].grid(row=row, column=1, columnspan=2, sticky=tk.EW) where = self["where"].get() if where != "Use all rows": self["query-column"].grid(row=row, column=3, sticky=tk.EW) self["query-op"].grid(row=row, column=4) op = self["query-op"].get() if "empty" not in op: self["query-value"].grid(row=row, column=5, sticky=tk.EW) frame.columnconfigure(5, weight=1) elif loop_type == "For systems in the database": row += 1 self["criteria"].grid(row=row, column=1, columnspan=3, sticky=tk.NSEW) frame.rowconfigure(row, weight=1, minsize=100) row += 1 self["default configuration"].grid( row=row, column=0, columnspan=2, sticky=tk.EW ) op = self["default configuration"].get() if "name" in op: self["configuration name"].grid( row=row, column=2, columnspan=2, sticky=tk.EW ) frame.columnconfigure(3, weight=1) else: raise RuntimeError("Don't recognize the loop_type {}".format(loop_type)) row += 1 self["errors"].grid(row=row, column=0, columnspan=4, sticky=tk.W) row += 1 frame.columnconfigure(0, minsize=40)
[docs] def right_click(self, event): """Probably need to add our dialog...""" super().right_click(event) self.popup_menu.add_command(label="Edit..", command=self.edit) self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
[docs] def default_edge_subtype(self): """Return the default subtype of the edge. Usually this is 'next' but for nodes with two or more edges leaving them, such as a loop, this method will return an appropriate default for the current edge. For example, by default the first edge emanating from a loop-node is the 'loop' edge; the second, the 'exit' edge. A return value of 'too many' indicates that the node exceeds the number of allowed exit edges. """ # how many outgoing edges are there? n_edges = len(self.tk_flowchart.edges(self, direction="out")) logger.debug("loop.default_edge_subtype, n_edges = {}".format(n_edges)) if n_edges == 0: return "loop" elif n_edges == 1: return "exit" else: return "too many"
[docs] def next_anchor(self): """Return where the next node should be positioned. The default is <gap> below the 's' anchor point. """ # how many outgoing edges are there? n_edges = len(self.tk_flowchart.edges(self, direction="out")) if n_edges == 0: return "e" elif n_edges == 1: return "s" else: return "sw"