diff --git a/flatland/utils/editor.py b/flatland/utils/editor.py index f44deda7dc0d63512ee9926825a987b76409f2b5..21c1e64b8ce23b70d21dae032eb75f410dbddcca 100644 --- a/flatland/utils/editor.py +++ b/flatland/utils/editor.py @@ -4,6 +4,8 @@ import time from collections import deque from matplotlib import pyplot as plt import threading +import os + # from contextlib import redirect_stdout # import os # import sys @@ -19,107 +21,207 @@ import flatland.utils.rendertools as rt from examples.play_model import Player from flatland.envs.env_utils import mirror +import ipywidgets +from ipywidgets import IntSlider, VBox, HBox, Checkbox, Output, Text +import jpy_canvas + + +class EditorMVC(object): + def __init__(self, env=None): + + if env is None: + env = RailEnv(width=10, + height=10, + rail_generator=random_rail_generator(cell_type_relative_proportion=[1, 1] + [0.5] * 6), + number_of_agents=0, + obs_builder_object=TreeObsForRailEnv(max_depth=2)) + + env.reset() + + self.editor = EditorModel(env) + self.editor.view = self.view = View(self.editor) + self.view.controller = self.editor.controller = self.controller = Controller(self.editor, self.view) + self.view.init_canvas() + self.view.init_widgets() # has to be done after controller + class View(object): def __init__(self, editor): - self.editor = editor + self.editor = self.model = editor + + def display(self): + self.wOutput.clear_output() + return self.wMain + + def init_canvas(self): self.oRT = rt.RenderTool(self.editor.env) plt.figure(figsize=(10, 10)) self.oRT.renderEnv(spacing=False, arrows=False, sRailColor="gray", show=False) img = self.oRT.getImage() plt.clf() - import jpy_canvas - self.wid_img = jpy_canvas.Canvas(img) - - -class JupEditor(object): - def __init__(self, env, wid_img): - print("Correct editor") - self.env = env - self.wid_img = wid_img - - self.qEvents = deque() - - self.regen_size = 10 + self.wImage = jpy_canvas.Canvas(img) + self.yxSize = self.wImage.data.shape[:2] + self.writableData = np.copy(self.wImage.data) # writable copy of image - wid_img.data is somehow readonly + self.wImage.register_move(self.controller.on_mouse_move) + self.wImage.register_click(self.controller.on_click) # TODO: These are currently estimated values self.yxBase = array([6, 21]) # pixel offset - self.nPixCell = 700 / self.env.rail.width # 35 + self.nPixCell = 700 / self.model.env.rail.width # 35 + + def init_widgets(self): + # Radiobutton for drawmode - TODO: replace with shift/ctrl/alt keys + # self.wDrawMode = RadioButtons(options=["Draw", "Erase", "Origin", "Destination"]) + # self.wDrawMode.observe(self.editor.setDrawMode, names="value") + + # Debug checkbox - enable logging in the Output widget + self.wDebug = ipywidgets.Checkbox(description="Debug") + self.wDebug.observe(self.controller.setDebug, names="value") + + # Separate checkbox for mouse move events - they are very verbose + self.wDebug_move = Checkbox(description="Debug mouse move") + self.wDebug_move.observe(self.controller.setDebugMove, names="value") + + # This is like a cell widget where loggin goes + self.wOutput = Output() + + # Filename textbox + self.wFilename = Text(description="Filename") + self.wFilename.value = self.model.env_filename + self.wFilename.observe(self.controller.setFilename, names="value") + + # Size of environment when regenerating + self.wSize = IntSlider(value=10, min=5, max=30, step=5, description="Regen Size") + self.wSize.observe(self.controller.setRegenSize, names="value") + + # Progress bar intended for stepping in the background (not yet working) + self.wProg_steps = ipywidgets.IntProgress(value=0, min=0, max=20, step=1, description="Step") + + # abbreviated description of buttons and the methods they call + ldButtons = [ + dict(name="Refresh", method=self.controller.refresh), + dict(name="Clear", method=self.controller.clear), + dict(name="Regenerate", method=self.controller.regenerate), + dict(name="Load", method=self.controller.load), + dict(name="Save", method=self.controller.save), + dict(name="Step", method=self.controller.step), + dict(name="Run Steps", method=self.controller.start_run), + ] + + self.lwButtons = [] + for dButton in ldButtons: + wButton = ipywidgets.Button(description=dButton["name"]) + wButton.on_click(dButton["method"]) + self.lwButtons.append(wButton) + + self.wVbox_controls = VBox([ + self.wFilename, # self.wDrawMode, + *self.lwButtons, + self.wSize, + self.wDebug, self.wDebug_move, + self.wProg_steps + ]) + + self.wMain = HBox([self.wImage, self.wVbox_controls]) - self.lrcStroke = [] - self.iTransLast = -1 - self.gRCTrans = array([[-1, 0], [0, 1], [1, 0], [0, -1]]) # NESW in RC + def drawStroke(self): + pass + + def new_env(self): + self.oRT = rt.RenderTool(self.editor.env) - self.debug = False - self.debug_move = False - self.wid_output = None - self.drawMode = "Draw" - self.env_filename = "temp.npy" - self.set_env(env) - self.iAgent = None - self.player = None - self.thread = None + def redraw(self): + # TODO: bit of a hack - can we suppress the console messages from MPL at source? + # with redirect_stdout(stdout_dest): + with self.wOutput: + plt.figure(figsize=(10, 10)) + self.oRT.renderEnv(spacing=False, arrows=False, sRailColor="gray", show=False) + img = self.oRT.getImage() + plt.clf() + plt.close() + + self.wImage.data = img + self.writableData = np.copy(self.wImage.data) + return img + + def redisplayImage(self): + if self.writableData is not None: + # This updates the image in the browser to be the new edited version + self.wImage.data = self.writableData - def set_env(self, env): - self.env = env - self.yxBase = array([6, 21]) # pixel offset - self.nPixCell = 700 / self.env.rail.width # 35 - self.oRT = rt.RenderTool(env) + def drag_path_element(self, x, y): + # Draw a black square on the in-memory copy of the image + if x > 10 and x < self.yxSize[1] and y > 10 and y < self.yxSize[0]: + self.writableData[y-2:y+2, x-2:x+2, :] = 0 - def setDebug(self, dEvent): - self.debug = dEvent["new"] - self.log("Set Debug:", self.debug) + def xy_to_rc(self, x, y): + rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int) + return rcCell - def setDebugMove(self, dEvent): - self.debug_move = dEvent["new"] - self.log("Set DebugMove:", self.debug) + def log(self, *args, **kwargs): + if self.wOutput: + with self.wOutput: + print(*args, **kwargs) + else: + print(*args, **kwargs) - def setOutput(self, wid_output): - self.wid_output = wid_output - def setDrawMode(self, dEvent): - self.drawMode = dEvent["new"] +class Controller(object): + """ + Controller to handle incoming events from the ipywidgets + Updates the editor/model. + Calls the View directly for things which do not directly effect the model + (this means the mouse drag path before it is interpreted as transitions) + """ + def __init__(self, model, view): + self.editor = self.model = model + self.view = view + self.qEvents = deque() + self.drawMode = "Draw" + + def setModel(self, model): + self.model = model def on_click(self, wid, event): x = event['canvasX'] y = event['canvasY'] - rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int) + self.debug("debug:", event) - if self.drawMode == "Origin": - self.iAgent = self.env.add_agent(rcCell, rcCell, None) - self.drawMode = "Destination" - self.player = None # will need to start a new player + rcCell = self.view.xy_to_rc(x, y) - elif self.drawMode == "Destination" and self.iAgent is not None: - self.env.agents_target[self.iAgent] = rcCell - self.drawMode = "Origin" - - # self.log("agent", self.drawMode, self.iAgent, rcCell) + bShift = event["shiftKey"] + bCtrl = event["ctrlKey"] + if bCtrl and not bShift: + self.model.add_agent(rcCell) + elif bShift and bCtrl: + self.model.add_target(rcCell) - if self.debug: - self.log("debug:", event) - binTrans = self.env.rail.get_transitions(rcCell) - sbinTrans = format(binTrans, "#018b")[2:] - self.log("cell ", rcCell, "Transitions: ", binTrans, sbinTrans, - [sbinTrans[i:i+4] for i in range(0, len(sbinTrans), 4)]) + self.debug("click in cell", rcCell) + self.model.debug_cell(rcCell) - self.redraw() + def setDebug(self, dEvent): + self.model.setDebug(dEvent["new"]) + + def setDebugMove(self, dEvent): + self.model.setDebug_move(dEvent["new"]) + + def setDrawMode(self, dEvent): + self.drawMode = dEvent["new"] + + def setFilename(self, event): + self.model.setFilename(event["new"]) - def event_handler(self, wid, event): + def on_mouse_move(self, wid, event): """Mouse motion event handler for drawing. """ x = event['canvasX'] y = event['canvasY'] - env = self.env qEvents = self.qEvents - lrcStroke = self.lrcStroke - bRedrawn = False - writableData = None - if self.debug and (event["buttons"] > 0 or self.debug_move): - self.log("debug:", len(qEvents), len(lrcStroke), event) + if self.model.bDebug and (event["buttons"] > 0 or self.model.bDebug_move): + self.debug("debug:", len(qEvents), event) - assert wid == self.wid_img, "wid not same as wid_img" + # assert wid == self.wid_img, "wid not same as wid_img" # If the mouse is held down, enqueue an event in our own queue # The intention was to avoid too many redraws. @@ -134,66 +236,161 @@ class JupEditor(object): if len(qEvents) > 0: tNow = time.time() if tNow - qEvents[0][0] > 0.1: # wait before trying to draw - height, width = wid.data.shape[:2] - writableData = np.copy(self.wid_img.data) # writable copy of image - wid_img.data is somehow readonly + # height, width = wid.data.shape[:2] + # writableData = np.copy(self.wid_img.data) # writable copy of image - wid_img.data is somehow readonly + + # with self.wid_img.hold_sync(): - with self.wid_img.hold_sync(): - while len(qEvents) > 0: - t, x, y = qEvents.popleft() # get events from our queue + while len(qEvents) > 0: + t, x, y = qEvents.popleft() # get events from our queue + self.view.drag_path_element(x, y) + + # Translate and scale from x,y to integer row,col (note order change) + # rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int) + rcCell = self.view.xy_to_rc(x, y) + self.editor.drag_path_element(rcCell) + + # Store the row,col location of the click, if we have entered a new cell + # if len(lrcStroke) > 0: + # rcLast = lrcStroke[-1] + # if not np.array_equal(rcLast, rcCell): # only save at transition + # # print(y, x, rcCell) + # lrcStroke.append(rcCell) + # else: + # # This is the first cell in a mouse stroke + # lrcStroke.append(rcCell) + self.view.redisplayImage() + else: + self.model.mod_path(not event["shiftKey"]) + + def refresh(self, event): + self.debug("refresh") + self.view.redraw() + + def clear(self, event): + self.model.clear() + + def regenerate(self, event): + self.model.regenerate() + + def setRegenSize(self, event): + self.model.setRegenSize(event["new"]) + + def load(self, event): + self.model.load() - # Draw a black square - if x > 10 and x < width and y > 10 and y < height: - writableData[y-2:y+2, x-2:x+2, :] = 0 + def save(self, event): + self.model.save() + + def step(self, event): + self.model.step() + + def start_run(self, event): + self.model.start_run() + + def log(self, *args, **kwargs): + if self.view is None: + print(*args, **kwargs) + else: + self.view.log(*args, **kwargs) + + def debug(self, *args, **kwargs): + self.model.debug(*args, **kwargs) + + +class EditorModel(object): + def __init__(self, env): + self.view = None + self.env = env + self.regen_size = 10 + + self.lrcStroke = [] + self.iTransLast = -1 + self.gRCTrans = array([[-1, 0], [0, 1], [1, 0], [0, -1]]) # NESW in RC + + self.bDebug = False + self.bDebug_move = False + self.wid_output = None + self.drawMode = "Draw" + self.env_filename = "temp.npy" + self.set_env(env) + self.iAgent = None + self.player = None + self.thread = None + + def set_env(self, env): + """ + set a new env for the editor, used by load and regenerate. + """ + self.env = env + self.yxBase = array([6, 21]) # pixel offset + self.nPixCell = 700 / self.env.rail.width # 35 + self.oRT = rt.RenderTool(env) + + def setDebug(self, bDebug): + self.bDebug = bDebug + self.log("Set Debug:", self.bDebug) + + def setDebugMove(self, bDebug): + self.bDebug_move = bDebug + self.log("Set DebugMove:", self.bDebug_move) + + def setDrawMode(self, sDrawMode): + self.drawMode = sDrawMode + + def drag_path_element(self, rcCell): + """Mouse motion event handler for drawing. + """ + lrcStroke = self.lrcStroke - # Translate and scale from x,y to integer row,col (note order change) - rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int) - - # Store the row,col location of the click, if we have entered a new cell - if len(lrcStroke) > 0: - rcLast = lrcStroke[-1] - if not np.array_equal(rcLast, rcCell): # only save at transition - # print(y, x, rcCell) - lrcStroke.append(rcCell) - else: - # This is the first cell in a mouse stroke - lrcStroke.append(rcCell) + # Store the row,col location of the click, if we have entered a new cell + if len(lrcStroke) > 0: + rcLast = lrcStroke[-1] + if not np.array_equal(rcLast, rcCell): # only save at transition + lrcStroke.append(rcCell) + self.debug("lrcStroke ", len(lrcStroke), rcCell) + + else: + # This is the first cell in a mouse stroke + lrcStroke.append(rcCell) + self.debug("lrcStroke ", len(lrcStroke), rcCell) + def mod_path(self, bAddRemove): # This elif means we wait until all the mouse events have been processed (black square drawn) # before trying to draw rails. (We could change this behaviour) # Equivalent to waiting for mouse button to be lifted (and a mouse event is necessary: # the mouse may need to be moved) - elif len(lrcStroke) >= 3: - # If we have already touched 3 cells - # We have a transition into a cell, and out of it. - - if self.drawMode == "Draw": - bAddRemove = True - elif self.drawMode == "Erase": - bAddRemove = False + lrcStroke = self.lrcStroke + if len(lrcStroke) >= 2: + self.mod_rail_cell_seq(lrcStroke, bAddRemove) + self.redraw() + def mod_rail_cell_seq(self, lrcStroke, bAddRemove=True): + # If we have already touched 3 cells + # We have a transition into a cell, and out of it. + + if len(lrcStroke) >= 2: # If the first cell in a stroke is empty, add a deadend to cell 0 if self.env.rail.get_transitions(lrcStroke[0]) == 0: - self.add_rail_2cells(lrcStroke, bAddRemove, iCellToMod=0) + self.mod_rail_2cells(lrcStroke, bAddRemove, iCellToMod=0) - while len(lrcStroke) >= 3: - self.add_rail_3cells(lrcStroke, env, bAddRemove) + # Add transitions for groups of 3 cells + # hence inbound and outbound transitions for middle cell + while len(lrcStroke) >= 3: + self.mod_rail_3cells(lrcStroke, bAddRemove=bAddRemove) - # If final cell empty, insert deadend: - if len(lrcStroke) == 2 and (self.env.rail.get_transitions(lrcStroke[1]) == 0): - self.add_rail_2cells(lrcStroke, bAddRemove, iCellToMod=1) + # If final cell empty, insert deadend: + if len(lrcStroke) == 2: + if self.env.rail.get_transitions(lrcStroke[1]) == 0: + self.mod_rail_2cells(lrcStroke, bAddRemove, iCellToMod=1) - self.redraw() - bRedrawn = True + # now empty out the final two cells from the queue + lrcStroke.clear() - # only redraw with the dots/squares if necessary - if not bRedrawn and writableData is not None: - # This updates the image in the browser to be the new edited version - self.wid_img.data = writableData - - def add_rail_3cells(self, rcCells, bAddRemove=True, bPop=True): + def mod_rail_3cells(self, lrcStroke, bAddRemove=True, bPop=True): """ Add transitions for rail spanning three cells. - lrcCells -- list of 3 rc cells + lrcStroke -- list containing "stroke" of cells across grid bAddRemove -- whether to add (True) or remove (False) the transition The transition is added to or removed from the 2nd cell, consistent with entering from the 1st cell, and exiting into the 3rd. @@ -201,15 +398,15 @@ class JupEditor(object): eg rcCells [(3,4), (2,4), (2,5)] would result in the transitions N->E and W->S in cell (2,4). """ - rc3Cells = array(rcCells[:3]) # the 3 cells + rc3Cells = array(lrcStroke[:3]) # the 3 cells rcMiddle = rc3Cells[1] # the middle cell which we will update - bDeadend = np.all(rcCells[0] == rcCells[2]) # deadend means cell 0 == cell 2 + bDeadend = np.all(lrcStroke[0] == lrcStroke[2]) # deadend means cell 0 == cell 2 # Save the original state of the cell # oTransrcMiddle = self.env.rail.get_transitions(rcMiddle) # sTransrcMiddle = self.env.rail.cell_repr(rcMiddle) - # get the 2 row, col deltas between the 3 cells, eg [-1,0] = North + # get the 2 row, col deltas between the 3 cells, eg [[-1,0],[0,1]] = North, East rc2Trans = np.diff(rc3Cells, axis=0) # get the direction index for the 2 transitions @@ -246,9 +443,9 @@ class JupEditor(object): # self.log(rcMiddle, "Orig:", sTransrcMiddle, "Mod:", self.env.rail.cell_repr(rcMiddle)) if bPop: - rcCells.pop(0) # remove the first cell in the stroke + lrcStroke.pop(0) # remove the first cell in the stroke - def add_rail_2cells(self, lrcCells, bAddRemove=True, iCellToMod=0, bPop=False): + def mod_rail_2cells(self, lrcCells, bAddRemove=True, iCellToMod=0, bPop=False): """ Add transitions for rail between two cells lrcCells -- list of two rc cells @@ -283,31 +480,10 @@ class JupEditor(object): if bPop: lrcCells.pop(0) - def redraw(self, hide_stdout=True, update=True): - - # if hide_stdout: - # stdout_dest = os.devnull - # else: - # stdout_dest = sys.stdout + def redraw(self): + self.view.redraw() - # TODO: bit of a hack - can we suppress the console messages from MPL at source? - # with redirect_stdout(stdout_dest): - with self.wid_output: - plt.figure(figsize=(10, 10)) - self.oRT.renderEnv(spacing=False, arrows=False, sRailColor="gray", show=False) - img = self.oRT.getImage() - plt.clf() - plt.close() - - if update: - self.wid_img.data = img - return img - - def redraw_event(self, event): - img = self.redraw() - self.wid_img.data = img - - def clear(self, event): + def clear(self): self.env.rail.grid[:, :] = 0 self.env.number_of_agents = 0 self.env.agents_position = [] @@ -315,48 +491,62 @@ class JupEditor(object): self.env.agents_handles = [] self.env.agents_target = [] self.player = None - - self.redraw_event(event) + + self.redraw() def setFilename(self, filename): self.log("filename = ", filename, type(filename)) self.env_filename = filename - def setFilename_event(self, event): - self.setFilename(event["new"]) - - def load(self, event): - self.env.rail.load_transition_map(self.env_filename, override_gridsize=True) - self.fix_env() - self.set_env(self.env) - self.wid_img.data = self.redraw() + def load(self): + if os.path.exists(self.env_filename): + self.log("load file: ", self.env_filename) + self.env.rail.load_transition_map(self.env_filename, override_gridsize=True) + self.fix_env() + self.set_env(self.env) + self.redraw() + else: + self.log("File does not exist:", self.env_filename, " Working directory: ", os.getcwd()) - def save(self, event): - self.log("save to ", self.env_filename) + def save(self): + self.log("save to ", self.env_filename, " working dir: ", os.getcwd()) self.env.rail.save_transition_map(self.env_filename) - def regenerate_event(self, event): + def regenerate(self): + self.log("Regenerate size", self.regen_size) self.env = RailEnv(width=self.regen_size, height=self.regen_size, rail_generator=random_rail_generator(cell_type_relative_proportion=[1, 1] + [0.5] * 6), number_of_agents=self.env.number_of_agents, obs_builder_object=TreeObsForRailEnv(max_depth=2)) self.env.reset(regen_rail=True) + self.fix_env() self.set_env(self.env) self.player = Player(self.env) + self.view.new_env() self.redraw() - def setRegenSize_event(self, event): - self.regen_size = event["new"] + def setRegenSize(self, size): + self.regen_size = size - def step_event(self, event=None): + def add_agent(self, rcCell): + self.iAgent = self.env.add_agent(rcCell, rcCell, None) + self.player = None # will need to start a new player + self.redraw() + + def add_target(self, rcCell): + if self.iAgent is not None: + self.env.agents_target[self.iAgent] = rcCell + self.redraw() + + def step(self): if self.player is None: self.player = Player(self.env) self.env.reset(regen_rail=False, replace_agents=False) self.player.step() self.redraw() - def start_run_event(self, event=None): + def start_run(self): if self.thread is None: self.thread = threading.Thread(target=self.bg_updater, args=()) self.thread.start() @@ -367,7 +557,7 @@ class JupEditor(object): try: for i in range(20): # self.log("step ", i) - self.step_event() + self.step() time.sleep(0.2) finally: self.thread = None @@ -377,10 +567,17 @@ class JupEditor(object): self.env.height = self.env.rail.height def log(self, *args, **kwargs): - - if self.wid_output: - with self.wid_output: - print(*args, **kwargs) - else: + if self.view is None: print(*args, **kwargs) + else: + self.view.log(*args, **kwargs) + + def debug(self, *args, **kwargs): + if self.bDebug: + self.log(*args, **kwargs) + def debug_cell(self, rcCell): + binTrans = self.env.rail.get_transitions(rcCell) + sbinTrans = format(binTrans, "#018b")[2:] + self.debug("cell ", rcCell, "Transitions: ", binTrans, sbinTrans, + [sbinTrans[i:i+4] for i in range(0, len(sbinTrans), 4)]) diff --git a/notebooks/Editor2.ipynb b/notebooks/Editor2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..e2fa3060fe701bbd44992da8a3723f8936dd8a50 --- /dev/null +++ b/notebooks/Editor2.ipynb @@ -0,0 +1,232 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Rail Editor v0.2" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from numpy import array\n", + "import ipywidgets\n", + "import IPython\n", + "from IPython.core.display import display, HTML" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<style>.container { width:90% !important; }</style>" + ], + "text/plain": [ + "<IPython.core.display.HTML object>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(HTML(\"<style>.container { width:90% !important; }</style>\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cpu\n" + ] + } + ], + "source": [ + "from flatland.utils.editor import EditorMVC, EditorModel, View, Controller" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "<Figure size 720x720 with 0 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mvc = EditorMVC()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Instructions\n", + "- Drag to draw\n", + " - improved dead-ends\n", + "- Shift-Drag to erase rails\n", + " - erasing dead ends not yet automated - drag right across them\n", + "- ctrl-click to add agent\n", + " - direction chosen randomly to fit rail\n", + "- ctrl-shift-click to add target for last agent\n", + " - target can be moved by repeating " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a1884dec581441f4948f33ffa9cac53d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(Canvas(), VBox(children=(Text(value='temp.npy', description='Filename'), Button(description='Re…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mvc.view.display()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "647f8e9c1227404eb58393eca27af3c5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mvc.view.wOutput.clear_output()\n", + "mvc.view.wOutput" + ] + } + ], + "metadata": { + "hide_input": false, + "kernelspec": { + "display_name": "ve367", + "language": "python", + "name": "ve367" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + }, + "latex_envs": { + "LaTeX_envs_menu_present": true, + "autoclose": false, + "autocomplete": true, + "bibliofile": "biblio.bib", + "cite_by": "apalike", + "current_citInitial": 1, + "eqLabelWithNumbers": true, + "eqNumInitial": 1, + "hotkeys": { + "equation": "Ctrl-E", + "itemize": "Ctrl-I" + }, + "labels_anchors": false, + "latex_user_defs": false, + "report_style_numbering": false, + "user_envs_cfg": false + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}