Skip to content
Snippets Groups Projects
Commit 404d650a authored by hagrid67's avatar hagrid67
Browse files

Refactored editor as model-view-controller.

New notebook Editor2.ipynb.
parent 0b2b56f6
No related branches found
No related tags found
No related merge requests found
...@@ -4,6 +4,8 @@ import time ...@@ -4,6 +4,8 @@ import time
from collections import deque from collections import deque
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
import threading import threading
import os
# from contextlib import redirect_stdout # from contextlib import redirect_stdout
# import os # import os
# import sys # import sys
...@@ -19,107 +21,207 @@ import flatland.utils.rendertools as rt ...@@ -19,107 +21,207 @@ import flatland.utils.rendertools as rt
from examples.play_model import Player from examples.play_model import Player
from flatland.envs.env_utils import mirror 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): class View(object):
def __init__(self, editor): 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) self.oRT = rt.RenderTool(self.editor.env)
plt.figure(figsize=(10, 10)) plt.figure(figsize=(10, 10))
self.oRT.renderEnv(spacing=False, arrows=False, sRailColor="gray", show=False) self.oRT.renderEnv(spacing=False, arrows=False, sRailColor="gray", show=False)
img = self.oRT.getImage() img = self.oRT.getImage()
plt.clf() plt.clf()
import jpy_canvas self.wImage = jpy_canvas.Canvas(img)
self.wid_img = 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)
class JupEditor(object): self.wImage.register_click(self.controller.on_click)
def __init__(self, env, wid_img):
print("Correct editor")
self.env = env
self.wid_img = wid_img
self.qEvents = deque()
self.regen_size = 10
# TODO: These are currently estimated values # TODO: These are currently estimated values
self.yxBase = array([6, 21]) # pixel offset 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 = [] def drawStroke(self):
self.iTransLast = -1 pass
self.gRCTrans = array([[-1, 0], [0, 1], [1, 0], [0, -1]]) # NESW in RC
def new_env(self):
self.oRT = rt.RenderTool(self.editor.env)
self.debug = False def redraw(self):
self.debug_move = False # TODO: bit of a hack - can we suppress the console messages from MPL at source?
self.wid_output = None # with redirect_stdout(stdout_dest):
self.drawMode = "Draw" with self.wOutput:
self.env_filename = "temp.npy" plt.figure(figsize=(10, 10))
self.set_env(env) self.oRT.renderEnv(spacing=False, arrows=False, sRailColor="gray", show=False)
self.iAgent = None img = self.oRT.getImage()
self.player = None plt.clf()
self.thread = None 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): def drag_path_element(self, x, y):
self.env = env # Draw a black square on the in-memory copy of the image
self.yxBase = array([6, 21]) # pixel offset if x > 10 and x < self.yxSize[1] and y > 10 and y < self.yxSize[0]:
self.nPixCell = 700 / self.env.rail.width # 35 self.writableData[y-2:y+2, x-2:x+2, :] = 0
self.oRT = rt.RenderTool(env)
def setDebug(self, dEvent): def xy_to_rc(self, x, y):
self.debug = dEvent["new"] rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int)
self.log("Set Debug:", self.debug) return rcCell
def setDebugMove(self, dEvent): def log(self, *args, **kwargs):
self.debug_move = dEvent["new"] if self.wOutput:
self.log("Set DebugMove:", self.debug) with self.wOutput:
print(*args, **kwargs)
else:
print(*args, **kwargs)
def setOutput(self, wid_output):
self.wid_output = wid_output
def setDrawMode(self, dEvent): class Controller(object):
self.drawMode = dEvent["new"] """
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): def on_click(self, wid, event):
x = event['canvasX'] x = event['canvasX']
y = event['canvasY'] y = event['canvasY']
rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int) self.debug("debug:", event)
if self.drawMode == "Origin": rcCell = self.view.xy_to_rc(x, y)
self.iAgent = self.env.add_agent(rcCell, rcCell, None)
self.drawMode = "Destination"
self.player = None # will need to start a new player
elif self.drawMode == "Destination" and self.iAgent is not None: bShift = event["shiftKey"]
self.env.agents_target[self.iAgent] = rcCell bCtrl = event["ctrlKey"]
self.drawMode = "Origin" if bCtrl and not bShift:
self.model.add_agent(rcCell)
# self.log("agent", self.drawMode, self.iAgent, rcCell) elif bShift and bCtrl:
self.model.add_target(rcCell)
if self.debug: self.debug("click in cell", rcCell)
self.log("debug:", event) self.model.debug_cell(rcCell)
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.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. """Mouse motion event handler for drawing.
""" """
x = event['canvasX'] x = event['canvasX']
y = event['canvasY'] y = event['canvasY']
env = self.env
qEvents = self.qEvents qEvents = self.qEvents
lrcStroke = self.lrcStroke
bRedrawn = False
writableData = None
if self.debug and (event["buttons"] > 0 or self.debug_move): if self.model.bDebug and (event["buttons"] > 0 or self.model.bDebug_move):
self.log("debug:", len(qEvents), len(lrcStroke), event) 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 # If the mouse is held down, enqueue an event in our own queue
# The intention was to avoid too many redraws. # The intention was to avoid too many redraws.
...@@ -134,66 +236,161 @@ class JupEditor(object): ...@@ -134,66 +236,161 @@ class JupEditor(object):
if len(qEvents) > 0: if len(qEvents) > 0:
tNow = time.time() tNow = time.time()
if tNow - qEvents[0][0] > 0.1: # wait before trying to draw if tNow - qEvents[0][0] > 0.1: # wait before trying to draw
height, width = wid.data.shape[:2] # height, width = wid.data.shape[:2]
writableData = np.copy(self.wid_img.data) # writable copy of image - wid_img.data is somehow readonly # 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:
while len(qEvents) > 0: t, x, y = qEvents.popleft() # get events from our queue
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 def save(self, event):
if x > 10 and x < width and y > 10 and y < height: self.model.save()
writableData[y-2:y+2, x-2:x+2, :] = 0
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) # Store the row,col location of the click, if we have entered a new cell
rcCell = ((array([y, x]) - self.yxBase) / self.nPixCell).astype(int) if len(lrcStroke) > 0:
rcLast = lrcStroke[-1]
# Store the row,col location of the click, if we have entered a new cell if not np.array_equal(rcLast, rcCell): # only save at transition
if len(lrcStroke) > 0: lrcStroke.append(rcCell)
rcLast = lrcStroke[-1] self.debug("lrcStroke ", len(lrcStroke), rcCell)
if not np.array_equal(rcLast, rcCell): # only save at transition
# print(y, x, rcCell) else:
lrcStroke.append(rcCell) # This is the first cell in a mouse stroke
else: lrcStroke.append(rcCell)
# This is the first cell in a mouse stroke self.debug("lrcStroke ", len(lrcStroke), rcCell)
lrcStroke.append(rcCell)
def mod_path(self, bAddRemove):
# This elif means we wait until all the mouse events have been processed (black square drawn) # 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) # 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: # Equivalent to waiting for mouse button to be lifted (and a mouse event is necessary:
# the mouse may need to be moved) # the mouse may need to be moved)
elif len(lrcStroke) >= 3: lrcStroke = self.lrcStroke
# If we have already touched 3 cells if len(lrcStroke) >= 2:
# We have a transition into a cell, and out of it. self.mod_rail_cell_seq(lrcStroke, bAddRemove)
self.redraw()
if self.drawMode == "Draw":
bAddRemove = True
elif self.drawMode == "Erase":
bAddRemove = False
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 the first cell in a stroke is empty, add a deadend to cell 0
if self.env.rail.get_transitions(lrcStroke[0]) == 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: # Add transitions for groups of 3 cells
self.add_rail_3cells(lrcStroke, env, bAddRemove) # 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 final cell empty, insert deadend:
if len(lrcStroke) == 2 and (self.env.rail.get_transitions(lrcStroke[1]) == 0): if len(lrcStroke) == 2:
self.add_rail_2cells(lrcStroke, bAddRemove, iCellToMod=1) if self.env.rail.get_transitions(lrcStroke[1]) == 0:
self.mod_rail_2cells(lrcStroke, bAddRemove, iCellToMod=1)
self.redraw() # now empty out the final two cells from the queue
bRedrawn = True lrcStroke.clear()
# only redraw with the dots/squares if necessary def mod_rail_3cells(self, lrcStroke, bAddRemove=True, bPop=True):
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):
""" """
Add transitions for rail spanning three cells. 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 bAddRemove -- whether to add (True) or remove (False) the transition
The transition is added to or removed from the 2nd cell, consistent with The transition is added to or removed from the 2nd cell, consistent with
entering from the 1st cell, and exiting into the 3rd. entering from the 1st cell, and exiting into the 3rd.
...@@ -201,15 +398,15 @@ class JupEditor(object): ...@@ -201,15 +398,15 @@ class JupEditor(object):
eg rcCells [(3,4), (2,4), (2,5)] would result in the transitions eg rcCells [(3,4), (2,4), (2,5)] would result in the transitions
N->E and W->S in cell (2,4). 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 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 # Save the original state of the cell
# oTransrcMiddle = self.env.rail.get_transitions(rcMiddle) # oTransrcMiddle = self.env.rail.get_transitions(rcMiddle)
# sTransrcMiddle = self.env.rail.cell_repr(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) rc2Trans = np.diff(rc3Cells, axis=0)
# get the direction index for the 2 transitions # get the direction index for the 2 transitions
...@@ -246,9 +443,9 @@ class JupEditor(object): ...@@ -246,9 +443,9 @@ class JupEditor(object):
# self.log(rcMiddle, "Orig:", sTransrcMiddle, "Mod:", self.env.rail.cell_repr(rcMiddle)) # self.log(rcMiddle, "Orig:", sTransrcMiddle, "Mod:", self.env.rail.cell_repr(rcMiddle))
if bPop: 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 Add transitions for rail between two cells
lrcCells -- list of two rc cells lrcCells -- list of two rc cells
...@@ -283,31 +480,10 @@ class JupEditor(object): ...@@ -283,31 +480,10 @@ class JupEditor(object):
if bPop: if bPop:
lrcCells.pop(0) lrcCells.pop(0)
def redraw(self, hide_stdout=True, update=True): def redraw(self):
self.view.redraw()
# if hide_stdout:
# stdout_dest = os.devnull
# else:
# stdout_dest = sys.stdout
# TODO: bit of a hack - can we suppress the console messages from MPL at source? def clear(self):
# 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):
self.env.rail.grid[:, :] = 0 self.env.rail.grid[:, :] = 0
self.env.number_of_agents = 0 self.env.number_of_agents = 0
self.env.agents_position = [] self.env.agents_position = []
...@@ -315,48 +491,62 @@ class JupEditor(object): ...@@ -315,48 +491,62 @@ class JupEditor(object):
self.env.agents_handles = [] self.env.agents_handles = []
self.env.agents_target = [] self.env.agents_target = []
self.player = None self.player = None
self.redraw_event(event) self.redraw()
def setFilename(self, filename): def setFilename(self, filename):
self.log("filename = ", filename, type(filename)) self.log("filename = ", filename, type(filename))
self.env_filename = filename self.env_filename = filename
def setFilename_event(self, event): def load(self):
self.setFilename(event["new"]) if os.path.exists(self.env_filename):
self.log("load file: ", self.env_filename)
def load(self, event): self.env.rail.load_transition_map(self.env_filename, override_gridsize=True)
self.env.rail.load_transition_map(self.env_filename, override_gridsize=True) self.fix_env()
self.fix_env() self.set_env(self.env)
self.set_env(self.env) self.redraw()
self.wid_img.data = self.redraw() else:
self.log("File does not exist:", self.env_filename, " Working directory: ", os.getcwd())
def save(self, event): def save(self):
self.log("save to ", self.env_filename) self.log("save to ", self.env_filename, " working dir: ", os.getcwd())
self.env.rail.save_transition_map(self.env_filename) 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, self.env = RailEnv(width=self.regen_size,
height=self.regen_size, height=self.regen_size,
rail_generator=random_rail_generator(cell_type_relative_proportion=[1, 1] + [0.5] * 6), rail_generator=random_rail_generator(cell_type_relative_proportion=[1, 1] + [0.5] * 6),
number_of_agents=self.env.number_of_agents, number_of_agents=self.env.number_of_agents,
obs_builder_object=TreeObsForRailEnv(max_depth=2)) obs_builder_object=TreeObsForRailEnv(max_depth=2))
self.env.reset(regen_rail=True) self.env.reset(regen_rail=True)
self.fix_env()
self.set_env(self.env) self.set_env(self.env)
self.player = Player(self.env) self.player = Player(self.env)
self.view.new_env()
self.redraw() self.redraw()
def setRegenSize_event(self, event): def setRegenSize(self, size):
self.regen_size = event["new"] 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: if self.player is None:
self.player = Player(self.env) self.player = Player(self.env)
self.env.reset(regen_rail=False, replace_agents=False) self.env.reset(regen_rail=False, replace_agents=False)
self.player.step() self.player.step()
self.redraw() self.redraw()
def start_run_event(self, event=None): def start_run(self):
if self.thread is None: if self.thread is None:
self.thread = threading.Thread(target=self.bg_updater, args=()) self.thread = threading.Thread(target=self.bg_updater, args=())
self.thread.start() self.thread.start()
...@@ -367,7 +557,7 @@ class JupEditor(object): ...@@ -367,7 +557,7 @@ class JupEditor(object):
try: try:
for i in range(20): for i in range(20):
# self.log("step ", i) # self.log("step ", i)
self.step_event() self.step()
time.sleep(0.2) time.sleep(0.2)
finally: finally:
self.thread = None self.thread = None
...@@ -377,10 +567,17 @@ class JupEditor(object): ...@@ -377,10 +567,17 @@ class JupEditor(object):
self.env.height = self.env.rail.height self.env.height = self.env.rail.height
def log(self, *args, **kwargs): def log(self, *args, **kwargs):
if self.view is None:
if self.wid_output:
with self.wid_output:
print(*args, **kwargs)
else:
print(*args, **kwargs) 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)])
%% Cell type:markdown id: tags:
# Rail Editor v0.2
%% Cell type:code id: tags:
``` python
import numpy as np
from numpy import array
import ipywidgets
import IPython
from IPython.core.display import display, HTML
```
%% Cell type:code id: tags:
``` python
display(HTML("<style>.container { width:90% !important; }</style>"))
```
%% Output
%% Cell type:code id: tags:
``` python
from flatland.utils.editor import EditorMVC, EditorModel, View, Controller
```
%% Output
cpu
%% Cell type:code id: tags:
``` python
mvc = EditorMVC()
```
%% Output
%% Cell type:markdown id: tags:
## Instructions
- Drag to draw
- improved dead-ends
- Shift-Drag to erase rails
- erasing dead ends not yet automated - drag right across them
- ctrl-click to add agent
- direction chosen randomly to fit rail
- ctrl-shift-click to add target for last agent
- target can be moved by repeating
%% Cell type:code id: tags:
``` python
mvc.view.display()
```
%% Output
%% Cell type:code id: tags:
``` python
mvc.view.wOutput.clear_output()
mvc.view.wOutput
```
%% Output
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment