Skip to content

Commit

Permalink
Merge pull request #18 from cgre-aachen/solve_issue#3
Browse files Browse the repository at this point in the history
'NoneType' object has no attribute 'dpi'
  • Loading branch information
danielsk78 authored Dec 26, 2020
2 parents 90f1247 + d4d7d41 commit 3a6a00a
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 93 deletions.
23 changes: 9 additions & 14 deletions sandbox/main_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def __init__(self, sensor: Sensor, projector: Projector, aruco: MarkerDetection
self._create_widgets()

self._loaded_frame = False
self._error_message = ''

#@property @TODO: test if this works
#def sb_params(self):
Expand All @@ -114,14 +115,11 @@ def update(self, **kwargs):
"""
self.sb_params['ax'] = self.projector.ax

#if not self.sb_params.get('del_contour'):
# if 'ContourLinesModule' in self.modules.keys():
# self.modules['ContourLinesModule'].delete_contourns(self.sb_params['ax'])
if self._loaded_frame:
frame = self.previous_frame #if loaded DEM the previous frame will have this information
self.sb_params['extent'] = [0, frame.shape[1], 0, frame.shape[0], frame.min(), frame.max()]
self.sb_params['same_frame'] = False #TODO: need to organize the usage of same_frame because is contradictory
plt.pause(0.2)
#plt.pause(0.2)
else:
frame = self.sensor.get_frame()
self.sb_params['extent'] = self.sensor.extent
Expand All @@ -134,13 +132,6 @@ def update(self, **kwargs):
else:
self.previous_frame = frame
self.sb_params['same_frame'] = False
#if not numpy.allclose(self.previous_frame, frame, atol=self._atol, rtol=self._rtol, equal_nan=True):
# self.previous_frame = frame
# self.sb_params['same_frame'] = False
#else:
# frame = self.previous_frame
# self.sb_params['same_frame'] = True
#else: self.sb_params['same_frame'] = False
self.sb_params['frame'] = frame

#filter
Expand All @@ -149,12 +140,15 @@ def update(self, **kwargs):
df = self.Aruco.update()
else:
df = pd.DataFrame()
plt.pause(0.2)
#plt.pause(0.2)
self.lock.release()

self.sb_params['marker'] = df

try:
if self._error_message:
self.sb_params['ax'].texts = []
self._error_message = ''
self.lock.acquire()
_cmap = ['CmapModule'] if 'CmapModule' in self.modules.keys() else []
_contours = ['ContourLinesModule'] if 'ContourLinesModule' in self.modules.keys() else []
Expand All @@ -165,9 +159,10 @@ def update(self, **kwargs):
self.lock.release()
except Exception as e:
traceback.print_exc()
self._error_message = str(dateTimeObj) + str(e)
self._error_message = str(dateTimeObj) + str(type(e)) + str(e)
self.lock.release()
self.thread_status = 'stopped'
self.projector._write_text("Ups... Something went wrong. The Thread is paused...")

self.sb_params['ax'].set_xlim(xmin=self.sb_params.get('extent')[0], xmax=self.sb_params.get('extent')[1])
self.sb_params['ax'].set_ylim(ymin=self.sb_params.get('extent')[2], ymax=self.sb_params.get('extent')[3])
Expand All @@ -183,7 +178,7 @@ def load_frame(self, frame: numpy.ndarray = None):
"""
During the sandbox thread, if you want to fix a frame but not interrupt the thread, load the desired numpy array here.
This will change the flag self._loaded_frame = False to True in the update function and stop the sensor frame adquisition.
To stop this pass frame = None or change the flag self._loaded_frame to False.
To stop this, pass: frame = None or change the flag self._loaded_frame to False.
Args:
frame: numpy.ndarray: must be a matrix of desired resolution
Returns:
Expand Down
5 changes: 4 additions & 1 deletion sandbox/projector/contourlines.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def __init__(self, contours=True, contours_step=100,

self.contours_step_minor = contours_step_minor
self.contours_width_minor = contours_width_minor
#TODO: fix for issue #3 ?
self._pause = 0.5

def update(self, sb_params: dict):
active = sb_params.get('active_contours')
Expand Down Expand Up @@ -102,8 +104,9 @@ def update(self, sb_params: dict):
self.add_major_contours(frame, ax, extent[:4])
if self.minor_contours:
self.add_minor_contours(frame, ax, extent[:4])
if self.contours_label:
if self.contours_label and self.contours:
self.add_label_contours(ax)
#plt.pause(self._pause) #TODO: error from issue #3 seems to be originated from the text on the contour lines
else:
if self._active:
self.delete_contourns(ax)
Expand Down
151 changes: 78 additions & 73 deletions sandbox/projector/projector.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import panel as pn
import matplotlib
from matplotlib.figure import Figure
from matplotlib.axes import Axes
import matplotlib.pyplot as plt
import json
from sandbox import _calibration_dir
Expand Down Expand Up @@ -35,14 +37,14 @@ class Projector(object):
}
'''

def __init__(self, calibprojector: str = None, use_panel: bool = True, p_width=1280, p_height=800, **kwargs): #Native resolution of projector
def __init__(self, calibprojector: str = None, use_panel: bool = True, p_width=1280, p_height=800):
"""
Args:
calibprojector:
use_panel:
use_panel: Automatically display
p_width: x native resolution of the projector
p_height: y native resolution of the projector
"""
#pn.extension()
self.version = '2.0.p'
self.ax = None
self.figure = None
Expand Down Expand Up @@ -70,6 +72,10 @@ def __init__(self, calibprojector: str = None, use_panel: bool = True, p_width=1
self.hot = None
self.profile = None
self.sidebar = None
# This is to solve issue #3. Give 0.005 ms to each Text from ax.arists to be plotted
self._target_time = 0.005
self._paused_time = None

self.create_panel()
if use_panel is True:
self.start_server()
Expand All @@ -87,16 +93,12 @@ def create_panel(self):

self.figure = Figure(figsize=(self.p_frame_width / self.dpi, self.p_frame_height / self.dpi),
dpi=self.dpi)
#self.figure = plt.figure(figsize=(self.p_frame_width / self.dpi, self.p_frame_height / self.dpi),
# dpi=self.dpi)
#self.ax = plt.gca()
self.ax = plt.Axes(self.figure, [0., 0., 1., 1.])
self.ax = Axes(self.figure, [0., 0., 1., 1.])
self.figure.add_axes(self.ax)
self.ax.set_axis_off()
self.ax.get_xaxis().set_visible(False)
self.ax.get_yaxis().set_visible(False)


self.frame = pn.pane.Matplotlib(self.figure,
width=self.p_frame_width,
height=self.p_frame_height,
Expand Down Expand Up @@ -142,22 +144,27 @@ def create_panel(self):
sizing_mode='fixed',
css_classes=['panel']
)
#TODO: Super-dirty fix
#self._replace_figure_with_pyplot()
#self._paint_logo()

return True

def _paint_logo(self):
self.ax.texts=[]
self.ax.annotate("cgre-aachen / open_AR_Sandbox ", (self.p_frame_width/2, self.p_frame_height/2),
zorder=1)#c='k', fontsize=30)#, textcoords='offset pixels', xytext=(20, 20), zorder=21)
def _write_text(self, text: str = "cgre-aachen / open_AR_Sandbox"):
"""
Display a custom text to be displayed in the middle of the sandbox
Args:
text: message to display
Returns:
"""
self.ax.texts = []
x = (self.ax.get_xlim()[1] - self.ax.get_xlim()[0])/2
y = (self.ax.get_ylim()[1] - self.ax.get_ylim()[0])/2
self.ax.annotate(text, (x, y), zorder=1000, xycoords="data", fontsize=18, ha='center',
va='top', wrap=True)
self.trigger()

def _replace_figure_with_pyplot(self):
"""workaround to fix bug of no dpi"""
"""Deprecated!! workaround to fix bug of no dpi"""
figure = plt.figure(figsize=(self.p_frame_width / self.dpi, self.p_frame_height / self.dpi),
dpi=self.dpi)
dpi=self.dpi)
ax = plt.Axes(figure, [0., 0., 1., 1.])
figure.add_axes(ax)
plt.close(figure) # close figure to prevent inline display
Expand All @@ -167,19 +174,17 @@ def _replace_figure_with_pyplot(self):
self.frame.object = figure
self.trigger()


def start_server(self):
"""
Display the panel object in a new browser window
Returns:
"""
# Check for instances and close them?
self.panel.show(threaded=True)#, title="Sandbox frame!")#, port = 4242, use_reloader = False)
#TODO: check how can check if the port exist/open and overwrite it
self.panel.show(threaded=False) # , title="Sandbox frame!")#, port = 4242, use_reloader = False)
# TODO: check how can check if the port exist/open and overwrite it
print('Projector initialized and server started.\n'
'Please position the browser window accordingly and enter fullscreen!')

return True

def clear_axes(self):
Expand All @@ -192,28 +197,61 @@ def clear_axes(self):
self.trigger()
return True

def _clock(self):
"""
To to be sure that panel have enough time to display the figure he want. Solves issue #3
"""
ctext = [isinstance(text, matplotlib.text.Text) for text in self.ax.artists]
if True in ctext:
sec = len(ctext)*self._target_time # Give 0.005 ms to each Text from contours to be plotted
self._paused_time = sec
plt.pause(sec)
else:
self._paused_time = None

def trigger(self):
"""
Update the panel figure if modified
Returns:
"""
self.figure.canvas.draw_idle()
#self.figure.canvas.flush_events()
#self.ax.draw_idle()

# self.figure.canvas.draw_idle() # TODO: do we need this or not?
self.frame.param.trigger('object')
self._clock()
return True

def save_json(self, file: str = 'projector_calibration.json'):
"""
Saves the current state of the projector in a .JSON calibration file
Args:
file: address to save the calibration
Returns:
"""
with open(file, "w") as calibration_json:
data = {'version': self.version,
'p_width': self.p_width,
'p_height': self.p_height,
'p_frame_top': self.p_frame_top,
'p_frame_left': self.p_frame_left,
'p_frame_width': self.p_frame_width,
'p_frame_height': self.p_frame_height}
json.dump(data, calibration_json)
print('JSON configuration file saved:', str(file))
return True

def load_json(self, file: str):
"""
Load a calibration file (.JSON format) and actualizes the panel parameters
"""
Load a calibration file (.JSON format) and actualizes the panel parameters
Args:
file: address of the calibration to load
Returns:
"""
with open(file) as calibration_json:
with open(file) as calibration_json:
data = json.load(calibration_json)
if data['version'] == self.version:
self.p_width = data['p_width']
Expand All @@ -224,47 +262,25 @@ def load_json(self, file: str):
self.p_frame_height = data['p_frame_height']
print("JSON configuration loaded for projector.")
else:
print("JSON configuration incompatible.\nPlease select a valid calibration file or start a new calibration!")

def save_json(self, file: str = 'projector_calibration.json'):
"""
Saves the current state of the projector in a .JSON calibration file
Args:
file: address to save the calibration
Returns:
"""
with open(file, "w") as calibration_json:
data = {'version': self.version,
'p_width': self.p_width,
'p_height': self.p_height,
'p_frame_top': self.p_frame_top,
'p_frame_left': self.p_frame_left,
'p_frame_width': self.p_frame_width,
'p_frame_height': self.p_frame_height}
json.dump(data, calibration_json)
print('JSON configuration file saved:', str(file))
print("JSON configuration incompatible." +
"\nPlease select a valid calibration file or start a new calibration!")
return True

def calibrate_projector(self):
self._create_widgets()
panel = pn.Column("### Projector dashboard arrangement",
self._widget_p_frame_top,
self._widget_p_frame_left,
self._widget_p_frame_width,
self._widget_p_frame_height,
#self._widget_p_enable_auto_calibration,
#self._widget_p_automatic_calibration,)
'<b>Save file<b>',
self._widget_json_filename,
self._widget_json_save
)
self._widget_p_frame_top,
self._widget_p_frame_left,
self._widget_p_frame_width,
self._widget_p_frame_height,
'<b>Save file<b>',
self._widget_json_filename,
self._widget_json_save
)
return panel

def _create_widgets(self):

# projector widgets and links

self._widget_p_frame_top = pn.widgets.IntSlider(name='Main frame top margin',
value=self.p_frame_top,
start=0,
Expand All @@ -289,16 +305,6 @@ def _create_widgets(self):
end=self.p_height)
self._widget_p_frame_height.link(self.frame, callbacks={'value': self._callback_p_frame_height})

# Auto- Calibration widgets

#self._widget_p_enable_auto_calibration = pn.widgets.Checkbox(name='Enable Automatic Calibration', value=False)
#self._widget_p_enable_auto_calibration.param.watch(self._callback_enable_auto_calibration, 'value',
# onlychanged=False)

#self._widget_p_automatic_calibration = pn.widgets.Button(name="Run", button_type="success")
#self._widget_p_automatic_calibration.param.watch(self._callback_automatic_calibration, 'clicks',
# onlychanged=False)

self._widget_json_filename = pn.widgets.TextInput(name='Choose a calibration filename:')
self._widget_json_filename.param.watch(self._callback_json_filename, 'value', onlychanged=False)
self._widget_json_filename.value = _calibration_dir + 'my_projector_calibration.json'
Expand Down Expand Up @@ -337,4 +343,3 @@ def _callback_json_filename(self, event):
def _callback_json_save(self, event):
if self.json_filename is not None:
self.save_json(file=self.json_filename)

10 changes: 5 additions & 5 deletions tests/test_main_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@ def test_bug_no_dpi_no_aruco():
_calibprojector = _calibration_dir + "my_projector_calibration.json"
_calibsensor = _calibration_dir + "my_sensor_calibration.json"
from sandbox.sensor import Sensor
sensor = Sensor(calibsensor=_calibsensor, name="kinect_v2")
sensor = Sensor(calibsensor=_calibsensor, name="dummy")
from sandbox.projector import Projector
projector = Projector(calibprojector=_calibprojector)
projector = Projector(calibprojector=_calibprojector, use_panel = False)
# Initialize the aruco detection
from sandbox.main_thread import MainThread
main = MainThread(sensor=sensor, projector=projector)
# Start the thread
main.run()
main.sb_params
main.update()
projector.trigger()

def test_with_gempy():
from sandbox import _calibration_dir, _test_data
Expand Down Expand Up @@ -160,4 +160,4 @@ def test_topo_module():
mainT.add_module(name='Topo', module=module)
module.sea = True
module.sea_contour = True
mainT.update()
mainT.update()

0 comments on commit 3a6a00a

Please sign in to comment.