class Signals: layout = Signal('layout', arg_types=(object,)) sample = Signal('sample', arg_types=(object,)) next_port =Signal('next-port', arg_types=(object,)) ports = Signal('ports', arg_types=(object,)) containers = Signal('containers', arg_types=(object,)) status = Signal('status', arg_types=(object,)) failure = Signal('failure', arg_types=(object,))
class Signals: temp = Signal('temp', arg_types=(float, )) level = Signal('level', arg_types=(float, )) sample = Signal('sample', arg_types=(float, )) shield = Signal('shield', arg_types=(float, )) pos = Signal('position', arg_types=(object, ))
class Signals: result = Signal('result', arg_types=(int, object)) grid = Signal('grid', arg_types=(object, ))
class ImageWidget(Gtk.DrawingArea): image_loaded = Signal("image-loaded", arg_types=()) def __init__(self, size): super().__init__() self.surface = None self.is_rubber_banding = False self.is_shifting = False self.is_measuring = False self.pseudocolor = True self.image_loaded = False self.show_histogram = False self._canvas_is_clean = False self.display_gamma = 1.0 self.gamma = 0 self.scale = 1.0 self.extents_back = [] self.extents_forward = [] self.extents = None self.select_reflections() self.set_events(Gdk.EventMask.EXPOSURE_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_HINT_MASK | Gdk.EventMask.VISIBILITY_NOTIFY_MASK | Gdk.EventMask.SCROLL_MASK) self.connect('visibility-notify-event', self.on_visibility_notify) self.connect('unmap', self.on_unmap) self.connect('motion-notify-event', self.on_mouse_motion) self.connect('button_press_event', self.on_mouse_press) self.connect('scroll-event', self.on_mouse_scroll) self.connect('button_release_event', self.on_mouse_release) self.set_size_request(size, size) self.palettes = { True: color_palette(cmaps.inferno), False: color_palette(cmaps.binary) } self.data_loader = DataLoader() self.data_loader.connect('new-image', self.on_data_loaded) def select_reflections(self, reflections=None): if reflections is not None: diff = (reflections[:, 2] - self.frame_number) frame_sel = (diff >= 0) & (diff < 1) frame_reflections = reflections[frame_sel] sel = numpy.abs(frame_reflections[:, 4:]).sum(1) > 0 indexed = frame_reflections[sel] unindexed = frame_reflections[~sel] self.indexed_spots = indexed self.unindexed_spots = unindexed else: self.indexed_spots = [] self.unindexed_spots = [] def open(self, filename): self.data_loader.open(filename) def show(self, frame): self.data_loader.show(frame) def on_data_loaded(self, obj): self.image_header = obj.header self.beam_x, self.beam_y = obj.header['beam_center'] self.pixel_size = obj.header['pixel_size'] self.distance = obj.header['distance'] self.wavelength = obj.header['wavelength'] self.pixel_data = obj.data.T self.raw_img = obj.image self.frame_number = obj.header['frame_number'] self.filename = obj.header['filename'] self.name = '{} [ {} ]'.format(obj.header['name'], self.frame_number) self.image_size = obj.header['detector_size'] self.create_surface() self.emit('image-loaded') def create_surface(self): self.image_width, self.image_height = self.image_size width = min(self.image_width, self.image_height) if not self.extents: self.extents = (1, 1, width - 2, width - 2) else: ox, oy, ow, oh = self.extents nx = min(ox, self.image_width) ny = min(oy, self.image_height) nw = nh = max( 16, min(ow, oh, self.image_width - nx, self.image_height - ny)) self.extents = (nx, ny, nw, nh) self.surface = cairo.ImageSurface.create_for_data( self.raw_img, cairo.FORMAT_ARGB32, self.image_width, self.image_height) self.image_loaded = True self.queue_draw() def get_image_info(self): return self.data_loader def get_line_profile(self, x1, y1, x2, y2, width=1): x1, y1 = self.get_position(x1, y1)[:2] x2, y2 = self.get_position(x2, y2)[:2] vmin = 0 vmax = self.image_header['saturated_value'] coords = line(x1, y1, x2, y2) data = numpy.zeros((len(coords), 3)) n = 0 for ix, iy in coords: ix = max(1, ix) iy = max(1, iy) data[n][0] = n src = self.pixel_data[ix - width:ix + width, iy - width:iy + width] sel = (src > vmin) & (src < vmax) if sel.sum(): val = src[sel].mean() else: val = numpy.nan data[n][2] = val data[n][1] = self.radial_distance(ix, iy, coords[0][0], coords[0][1]) n += 1 return data[:, 1:] def make_histogram(self, data, show_axis=None, distance=True): color = colors.Category.CAT20C[0] matplotlib.rcParams.update({'font.size': 9.5}) figure = Figure(frameon=False, figsize=(4, 2), dpi=72, edgecolor=color) specs = matplotlib.gridspec.GridSpec(ncols=8, nrows=1, figure=figure) plot = figure.add_subplot(specs[0, 1:7]) plot.patch.set_alpha(1.0) adjust_spines(plot, ['left'], color) formatter = FormatStrFormatter('%g') plot.yaxis.set_major_formatter(formatter) plot.yaxis.set_major_locator(MaxNLocator(5)) if distance: plot.plot(data[:, 0], data[:, 1], lw=0.75) else: plot.vlines(data[:, 0], 0, data[:, 1]) plot.set_xlim(min(data[:, 0]), max(data[:, 0])) # Ask matplotlib to render the figure to a bitmap using the Agg backend canvas = FigureCanvasCairo(figure) width, height = canvas.get_width_height() renderer = RendererCairo(canvas.figure.dpi) renderer.set_width_height(width, height) self.plot_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) renderer.set_ctx_from_surface(self.plot_surface) canvas.figure.draw(renderer) self.show_histogram = True def draw_overlay_cairo(self, cr): # rubber band cr.set_line_width(2) cr.set_source_rgba(0.0, 0.5, 1.0, 1.0) if self.is_rubber_banding: x, y, w, h = bounding_box(self.rubber_x0, self.rubber_y0, self.rubber_x1, self.rubber_y1) cr.rectangle(x, y, w, h) cr.stroke() # cross x, y, w, h = self.extents radius = 16 * self.scale if (0 < (self.beam_x - x) < x + w) and (0 < (self.beam_y - y) < y + h): cx = int((self.beam_x - x) * self.scale) cy = int((self.beam_y - y) * self.scale) cr.move_to(cx - radius, cy) cr.line_to(cx + radius, cy) cr.stroke() cr.move_to(cx, cy - radius) cr.line_to(cx, cy + radius) cr.stroke() cr.arc(cx, cy, radius / 2, 0, 2.0 * numpy.pi) # measuring if self.is_measuring: cr.set_line_width(2) cr.move_to(self.meas_x0, self.meas_y0) cr.line_to(self.meas_x1, self.meas_y1) cr.stroke() # Filename pcontext = self.get_pango_context() alloc = self.get_allocation() font_desc = pcontext.get_font_description() cr.select_font_face(font_desc.get_family(), cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) cr.set_font_size(11) cr.move_to(6, alloc.height - 6) cr.show_text(self.name) cr.stroke() def draw_spots(self, cr): # draw spots x, y, w, h = self.extents cr.set_line_width(0.75) cr.set_source_rgba(0.0, 0.5, 1.0, 1.0) for i, spots in enumerate([self.indexed_spots, self.unindexed_spots]): if i == 0: cr.set_source_rgba(0.0, 0.5, 1.0, 0.75) else: cr.set_source_rgba(1.0, 0.5, 0.0, 0.75) for spot in spots: sx, sy = spot[:2] if (0 < (sx - x) < x + w) and (0 < (sy - y) < y + h): cx = int((sx - x) * self.scale) cy = int((sy - y) * self.scale) cr.arc(cx, cy, 16 * self.scale, 0, 2.0 * numpy.pi) cr.stroke() def go_back(self, full=False): if len(self.extents_back) > 0 and not full: self.extents = self.extents_back.pop() else: self.extents = (1, 1, self.image_width - 2, self.image_height - 2) self.extents_back = [] self.queue_draw() return len(self.extents_back) > 0 def colorize(self, color=False): if color: self.data_loader.set_colormap(1) else: self.data_loader.set_colormap(0) self.data_loader.setup() def reset_filters(self): self.data_loader.adjust() def get_position(self, x, y): if not self.image_loaded: return 0, 0, 0.0, 0 ix, iy = self.screen_to_image(x, y) Res = self.image_resolution(ix, iy) return ix, iy, Res, self.pixel_data[ix, iy] def save_surface(self, path): self.surface.write_to_png(path) logger.info('Image saved to PNG: {}'.format(path)) def screen_to_image(self, x, y): ix = int(x / self.scale) + self.extents[0] iy = int(y / self.scale) + self.extents[1] ix = max(1, min(ix, self.image_width - 2)) iy = max(1, min(iy, self.image_height - 2)) return ix, iy def image_resolution(self, x, y): displacement = self.pixel_size * math.sqrt((x - self.beam_x)**2 + (y - self.beam_y)**2) if self.distance == 0.0: angle = math.pi / 2 else: angle = 0.5 * math.atan(displacement / self.distance) if angle < 1e-3: angle = 1e-3 return self.wavelength / (2.0 * math.sin(angle)) def radial_distance(self, x0, y0, x1, y1): d = math.sqrt((x0 - x1)**2 + (y0 - y1)**2) * self.pixel_size return d # (self.wavelength * self.distance/d) def set_cursor_mode(self, cursor=None): window = self.get_window() if window is None: return if cursor is None: window.set_cursor(None) else: window.set_cursor(Gdk.Cursor.new(cursor)) Gtk.main_iteration() # callbacks def on_mouse_motion(self, widget, event): if not self.image_loaded: return False # print event.get_state().value_names alloc = self.get_allocation() w, h = alloc.width, alloc.height if 'GDK_BUTTON1_MASK' in event.get_state( ).value_names and self.is_rubber_banding: self.rubber_x1 = max(min(w - 1, event.x), 0) + 0.5 self.rubber_y1 = max(min(h - 1, event.y), 0) + 0.5 self.queue_draw() elif 'GDK_BUTTON2_MASK' in event.get_state( ).value_names and self.is_shifting: self.shift_x1 = event.x self.shift_y1 = event.y ox, oy, ow, oh = self.extents nx = int((self.shift_x0 - self.shift_x1) / self.scale) + ox ny = int((self.shift_y0 - self.shift_y1) / self.scale) + oy nw = ow nh = oh nx = max(0, nx) ny = max(0, ny) if nx + nw > self.image_width: nx = self.image_width - nw if ny + nh > self.image_height: ny = self.image_height - nh if self.extents != (nx, ny, nw, nh): self.extents = (nx, ny, nw, nh) self.shift_x0 = self.shift_x1 self.shift_y0 = self.shift_y1 self.queue_draw() elif 'GDK_BUTTON3_MASK' in event.get_state( ).value_names and self.is_measuring: self.meas_x1 = int(max(min(w - 1, event.x), 0)) + 0.5 self.meas_y1 = int(max(min(h - 1, event.y), 0)) + 0.5 self.queue_draw() return False def on_mouse_scroll(self, widget, event): if not self.image_loaded: return False if event.direction == Gdk.ScrollDirection.UP: self.data_loader.adjust(1) elif event.direction == Gdk.ScrollDirection.DOWN: self.data_loader.adjust(-1) def on_mouse_press(self, widget, event): self.show_histogram = False if not self.image_loaded: return False alloc = self.get_allocation() w, h = alloc.width, alloc.height if event.button == 1: self.rubber_x0 = max(min(w, event.x), 0) + 0.5 self.rubber_y0 = max(min(h, event.y), 0) + 0.5 self.rubber_x1, self.rubber_y1 = self.rubber_x0, self.rubber_y0 self.is_rubber_banding = True self.set_cursor_mode(Gdk.CursorType.TCROSS) elif event.button == 2: self.set_cursor_mode(Gdk.CursorType.FLEUR) self.shift_x0 = max(min(w, event.x), 0) self.shift_y0 = max(min(w, event.y), 0) self.shift_x1, self.shift_y1 = self.shift_x0, self.shift_y0 self._shift_start_extents = self.extents self.is_shifting = True self.set_cursor_mode(Gdk.CursorType.FLEUR) elif event.button == 3: self.meas_x0 = int(max(min(w, event.x), 0)) self.meas_y0 = int(max(min(h, event.y), 0)) self.meas_x1, self.meas_y1 = self.meas_x0, self.meas_y0 self.is_measuring = True self.set_cursor_mode(Gdk.CursorType.TCROSS) def on_mouse_release(self, widget, event): if not self.image_loaded: return False if self.is_rubber_banding: self.is_rubber_banding = False x0, y0 = self.screen_to_image(self.rubber_x0, self.rubber_y0) x1, y1 = self.screen_to_image(self.rubber_x1, self.rubber_y1) x, y, w, h = bounding_box(x0, y0, x1, y1) if min(w, h) < 10: return self.extents_back.append(self.extents) self.extents = (x, y, w, h) self.queue_draw() elif self.is_measuring: self.is_measuring = False # prevent zero-length lines self.histogram_data = self.get_line_profile( self.meas_x0, self.meas_y0, self.meas_x1, self.meas_y1, 2) if len(self.histogram_data) > 4: self.make_histogram(self.histogram_data, show_axis=[ 'left', ]) self.queue_draw() elif self.is_shifting: self.is_shifting = False self.extents_back.append(self._shift_start_extents) self.set_cursor_mode() def do_draw(self, cr): if self.surface is not None: alloc = self.get_allocation() width = min(alloc.width, alloc.height) self.scale = float(width) / self.extents[2] cr.save() cr.scale(self.scale, self.scale) cr.translate(-self.extents[0], -self.extents[1]) cr.set_source_surface(self.surface, 0, 0) if self.scale >= 1: cr.get_source().set_filter(cairo.FILTER_FAST) else: cr.get_source().set_filter(cairo.FILTER_GOOD) cr.paint() cr.restore() if self.show_histogram: px = alloc.width - self.plot_surface.get_width() - 10 py = alloc.height - self.plot_surface.get_height() - 10 cr.save() cr.set_source_surface(self.plot_surface, px, py) cr.paint() cr.restore() self.draw_overlay_cairo(cr) self.draw_spots(cr) self._canvas_is_clean = True def on_visibility_notify(self, obj, event): if event.get_state() == Gdk.VisibilityState.FULLY_OBSCURED: self.stopped = True else: self.stopped = False def on_unmap(self, obj): self.stopped = True
class Signals: mode = Signal("mode", arg_types=(object, ))
class Signals: changed = Signal("changed", arg_types=(float, )) percent = Signal("percent", arg_types=(float, ))
class Signals: ready = Signal("ready", arg_types=(bool, ))
class Signals: running = Signal('running', arg_types=()) collision = Signal('collision', arg_types=())
class Signals: changed = Signal('samples-changed', arg_types=(object,)) active = Signal('active-sample', arg_types=(object,)) selected = Signal('sample-selected', arg_types=(object,))
class Signals: changed = Signal("changed", arg_types=(bool,))
class Signals: selected = Signal("selected", arg_types=(str, ))
class Signals: sample_done = Signal('sample-done', arg_types=(str, )) sample_started = Signal('sample-started', arg_types=(str, ))
class Signals: message = Signal('message', arg_types=(str, str)) config = Signal('config', arg_types=(object,))
class Signals: message = Signal('message', arg_types=(str, ))
class Signals: updated = Signal("updated", arg_types=())
class Signals: high = Signal('high', arg_types=(bool, ))
class Signals: added = Signal('added', arg_types=(object, )) removed = Signal('removed', arg_types=(object, ))
class Signals: state = Signal("state", arg_types=(object, )) new_image = Signal("new-image", arg_types=(object, )) progress = Signal("progress", arg_types=(float, str))
class Signals: resized = Signal("resized", arg_types=(int, int))
class Signals: sig_int = Signal('sig-int', arg_types=(int, )) sig_float = Signal('sig-float', arg_types=(float, )) sig_str = Signal('sig-str', arg_types=(str, )) sig_bool = Signal('sig-bool', arg_types=(bool, ))
class Signals: report = Signal('new-report', arg_types=(str, object))
class Signals: sig_multi = Signal('sig-multi', arg_types=(int, bool, float))
class Signals: changed = Signal("changed", arg_types=(object,))
class Signals: found = Signal("found", arg_types=(int, int, float, str))
class Signals: changed = Signal("changed", arg_types=(object, )) count = Signal("count", arg_types=(float, ))
class Signals: result = Signal('result', arg_types=(object, ))
class Signals: new_image = Signal("new-image", arg_types=())
class Signals: new_point = Signal('new-point', arg_types=(object, )) new_row = Signal('new-row', arg_types=(int, )) message = Signal('message', arg_types=(str, ))
class Signals: changed = Signal("changed", arg_types=(float, )) starting = Signal("starting", arg_types=(bool, )) target = Signal("target", arg_types=(object, float)) done = Signal("done", arg_types=())
class Signals: deadtime = Signal("deadtime", arg_types=(float, ))