class Roby::GUI::RelationsCanvas
Constants
- COLORS
- DISPLAY_POLICIES
Attributes
A [object, object, relation] => GraphicsItem mapping of arrows
The set of relations that should be displayed
A set of events that are shown during only two calls of update
A DRbObject => GraphicsItem mapping
True if the finalized tasks should not be displayed
A [object, object, relation] => GraphicsItem mapping of arrows
@return [Hash] set of options that should be passed to
Graphviz#layout
The set of plans that should be displayed
The Qt::GraphicsScene we are manipulating
The set of objects that are selected for display in the :explicit display mode
A pool of arrows items used to display the event signalling
The set of objects that are to be shown at the last update
Public Class Methods
# File lib/roby/gui/relations_view/relations_canvas.rb, line 460 def initialize(plans) @scene = Qt::GraphicsScene.new super() @plans = plans.dup @display_plan_bounding_boxes = false @display_policy = :explicit @graphics = Hash.new @selected_objects = Set.new @visible_objects = Set.new @flashing_objects = Hash.new @arrows = Hash.new @free_arrows = Array.new @enabled_relations = Set.new @layout_relations = Set.new @relation_colors = Hash.new @relation_pens = Hash.new(Qt::Pen.new(Qt::Color.new(ARROW_COLOR))) @relation_brushes = Hash.new(Qt::Brush.new(Qt::Color.new(ARROW_COLOR))) @current_color = 0 @signal_arrows = [] @hide_finalized = true @layout_options = Hash.new default_colors = { Roby::TaskStructure::Dependency => 'grey', Roby::TaskStructure::PlannedBy => '#32ba21', Roby::TaskStructure::ExecutionAgent => '#5d95cf', Roby::TaskStructure::ErrorHandling => '#ff2727' } default_colors.each do |rel, color| update_relation_color(rel, color) end relation_pens[Roby::EventStructure::Signal] = Qt::Pen.new(Qt::Color.new('black')) relation_brushes[Roby::EventStructure::Signal] = Qt::Brush.new(Qt::Color.new('black')) relation_pens[Roby::EventStructure::Forwarding] = Qt::Pen.new(Qt::Color.new('black')) relation_pens[Roby::EventStructure::Forwarding].style = Qt::DotLine relation_brushes[Roby::EventStructure::Forwarding] = Qt::Brush.new(Qt::Color.new('black')) relation_brushes[Roby::EventStructure::Forwarding].style = Qt::DotLine enable_relation(Roby::TaskStructure::Dependency) enable_relation(Roby::TaskStructure::ExecutionAgent) enable_relation(Roby::TaskStructure::PlannedBy) end
Public Instance Methods
# File lib/roby/gui/relations_view/relations_canvas.rb, line 561 def [](item); graphics[item] end
Add object
to the list of objects temporarily displayed. If a block is given, the object is removed when the block returns false. Otherwise, it is removed at the next display update
If this method is called more than once for the same object, the object is removed when all blocks have returned false at least once
# File lib/roby/gui/relations_view/relations_canvas.rb, line 773 def add_flashing_object(object, &block) if block flashing_objects[object] ||= [] flashing_objects[object] << block else flashing_objects[object] ||= nil end if object.display_parent add_flashing_object(object.display_parent, &block) end create_or_get_item(object, false) end
returns the next color in COLORS
, cycles if at the end of the array
# File lib/roby/gui/relations_view/relations_canvas.rb, line 633 def allocate_color @current_color = (current_color + 1) % COLORS.size COLORS[current_color] end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 519 def apply_options(options) if enabled_relations = options['enabled_relations'] enabled_relations.each do |name| rel = constant(name) enable_relation(rel) end end apply_simple_option('show_ownership', options) apply_simple_option('removed_prefixes', options) apply_simple_option('hide_finalized', options) apply_simple_option('removed_prefixes', options) apply_simple_option('hidden_labels', options) apply_simple_option('display_policy', options) end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 534 def apply_simple_option(option_name, options) if options.has_key?(option_name) self.send("#{option_name}=", options[option_name]) end end
Creates or reuses an arrow object to represent the given relation
# File lib/roby/gui/relations_view/relations_canvas.rb, line 573 def arrow(from, to, rel, info, base_layer) id = [from, to, rel] if !(item = arrows[id]) if item = last_arrows.delete(id) arrows[id] = item else item = arrows[id] = (free_arrows.pop || scene.add_arrow(ARROW_SIZE)) item.z_value = base_layer - 1 item.pen = item.line.pen = relation_pens[rel] item.brush = relation_brushes[rel] end end GUI.arrow_set item, self[from], self[to] end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 1068 def clear arrows.dup.each_value(&method(:remove_graphics)) graphics.dup.each_value(&method(:remove_graphics)) arrows.clear free_arrows.clear last_arrows.clear graphics.clear signal_arrows.each do |arrow| arrow.visible = false end selected_objects.clear visible_objects.clear flashing_objects.clear scene.update(scene.scene_rect) end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 1059 def clear_arrows(object) arrows.delete_if do |(from, to, _), arrow| if from == object || to == object remove_graphics(arrow) true end end end
Removes the objects added with add_flashing_object
when they should be removed
# File lib/roby/gui/relations_view/relations_canvas.rb, line 789 def clear_flashing_objects removed_objects = [] flashing_objects.delete_if do |object, blocks| blocks.delete_if { |block| !block.call } if !blocks.empty? next end removed_objects << object end removed_objects.each do |object| if item = graphics[object] item.visible = displayed?(object) end end end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 748 def create_or_get_item(object, initial_selection) if !(item = graphics[object]) item = graphics[object] = object.display_create(self) if item if object.display_parent item.parent_item = self[object.display_parent] end yield(item) if block_given? if initial_selection selected_objects << object end end end item end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 673 def disable_relation(relation) return unless relation_enabled?(relation) @enabled_relations.delete(relation) arrows.each do |(_, _, rel), arrow| if rel == relation arrow.visible = false end end end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 734 def display_policy=(policy) if !DISPLAY_POLICIES.include?(policy) raise ArgumentError, "got #{policy.inspect} as a display policy, accepted values are #{DISPLAY_POLICIES.map(&:inspect).join(", ")}" end @display_policy = policy end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 741 def displayed?(object) if (parent = object.display_parent) && !displayed?(parent) return false end return visible_objects.include?(object) end
Display this relation
# File lib/roby/gui/relations_view/relations_canvas.rb, line 646 def enable_relation(relation) return if relation_enabled?(relation) @enabled_relations << relation arrows.each do |(_, _, rel), arrow| if rel == relation arrow.visible = true end end end
Returns a canvas object that represents this relation
# File lib/roby/gui/relations_view/relations_canvas.rb, line 568 def event_relation(form, to, rel, info) arrow(from, to, rel, info, EVENT_LAYER) end
Centers the view on the set of object found which matches regex
. If regex
is nil, ask one to the user
# File lib/roby/gui/relations_view/relations_canvas.rb, line 591 def find(regex = nil) unless regex regex = Qt::InputDialog.get_text main, 'Find objects in relation view', 'Object name' return unless regex && !regex.empty? end regex = /#{regex.to_str}/i if regex.respond_to?(:to_str) # Get the tasks and events matching the string objects = [] for p in plans objects.concat p.tasks. find_all { |object| displayed?(object) && regex === object.display_name(self) } objects.concat p.free_events. find_all { |object| displayed?(object) && regex === object.display_name(self) } end return if objects.empty? # Find the graphics items bb = objects.inject(Qt::RectF.new) do |bb, object| if item = self[object] item.selected = true bb | item.scene_bounding_rect | item.map_to_scene(item.children_bounding_rect).bounding_rect else bb end end bb.adjust(-FIND_MARGIN, -FIND_MARGIN, FIND_MARGIN, FIND_MARGIN) ui.graphics.fit_in_view bb, Qt::KeepAspectRatio scale = ui.graphics.matrix.m11 if scale > 1 ui.graphics.resetMatrix ui.graphics.scale 1, 1 end end
Don't use this relation at all
# File lib/roby/gui/relations_view/relations_canvas.rb, line 668 def ignore_relation(relation) disable_relation(relation) @layout_relations.delete(relation) end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 727 def layout_method return @layout_method if @layout_method "dot" end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 705 def layout_method=(new_method) return if new_method == @layout_method @layout_method = nil @layout_options = nil if new_method new_method =~ /^(\w+)(?: \[(.*)\])?$/ @layout_method = $1 if $2 @layout_options = $2.split(",").inject(Hash.new) do |h, v| k, v = v.split("=") h[k] = v h end end end display end
Use this relation for layout but not for display
See also ignore_relation
# File lib/roby/gui/relations_view/relations_canvas.rb, line 662 def layout_relation(relation) disable_relation(relation) @layout_relations << relation end
True if this relation should be used for layout
See also relation_enabled?
, layout_relation
, ignore_relation
# File lib/roby/gui/relations_view/relations_canvas.rb, line 643 def layout_relation?(relation); relation_enabled?(relation) || @layout_relations.include?(relation) end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 887 def make_graphics_visible(object) object = create_or_get_item(object, false) object.visible = true object end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 540 def object_of(item) id = item.data(0).to_string return if !id id = Integer(id) obj, _ = graphics.find do |obj, obj_item| obj.object_id == id end obj end
Sets the style on arrow
according to the event propagation type provided in flag
arrow
is the graphics item representing the relation and flag
is one of the PROPAG_ constant
# File lib/roby/gui/relations_view/relations_canvas.rb, line 811 def propagation_style(arrow, flag) unless defined? @@propagation_styles @@propagation_styles = Hash.new @@propagation_styles[true] = [Qt::Brush.new(Qt::Color.new('black')), Qt::Pen.new, (forward_pen = Qt::Pen.new)] forward_pen.style = Qt::DotLine @@propagation_styles[false] = [Qt::Brush.new(Qt::Color.new('black')), Qt::Pen.new, Qt::Pen.new] end arrow.brush, arrow.pen, arrow.line.pen = @@propagation_styles[flag] end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 686 def relation_color(relation) if !relation_colors.has_key?(relation) update_relation_color(relation, allocate_color) end relation_colors[relation] end
True if this relation should be displayed
# File lib/roby/gui/relations_view/relations_canvas.rb, line 639 def relation_enabled?(relation); @enabled_relations.include?(relation) end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 551 def relation_of(item) id = item.data(0).to_string arrows.each do |(from, to, rel), arrow| if arrow.data(0).to_string == id return from, to, rel end end nil end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 1053 def remove_graphics(item, scene = nil) return unless item scene ||= item.scene scene.remove_item(item) if scene end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 508 def save_options options = Hash.new options['enabled_relations'] = @enabled_relations.map(&:name) options['show_ownership'] = show_ownership options['hide_finalized'] = hide_finalized options['removed_prefixes'] = removed_prefixes.dup options['hidden_labels'] = hidden_labels.dup options['display_policy'] = display_policy options end
Returns a canvas object that represents this relation
# File lib/roby/gui/relations_view/relations_canvas.rb, line 564 def task_relation(from, to, rel, info) arrow(from, to, rel, info, TASK_LAYER) end
Update the display with new data that has come from the data stream.
It would be too complex at this stage to know if the plan has been updated, so the method always returns true
# File lib/roby/gui/relations_view/relations_canvas.rb, line 900 def update(time = nil) # Allow time to be a Qt::DateTime object, so that we can make it # a slot if time.kind_of?(Qt::DateTime) time = Time.at(Float(time.toMSecsSinceEpoch) / 1000) end enabled_relations << Roby::EventStructure::Signal << Roby::EventStructure::Forwarding if time @current_time = time end @last_arrows, @arrows = arrows, Hash.new @free_arrows ||= Array.new update_prefixes_removal clear_flashing_objects # The sets of tasks and events know to the data stream all_tasks = plans.inject(Set.new) do |all_tasks, plan| all_tasks.merge plan.tasks all_tasks.merge plan.finalized_tasks end all_events = plans.inject(Set.new) do |all_events, plan| all_events.merge plan.free_events all_events.merge plan.finalized_events end all_task_events = all_tasks.inject(Set.new) do |all_task_events, task| all_task_events.merge(task.bound_events.values) end # Remove the items for objects that don't exist anymore (graphics.keys.to_set - all_tasks - all_events - all_task_events).each do |obj| selected_objects.delete(obj) remove_graphics(graphics.delete(obj)) clear_arrows(obj) end # Create graphics items for all objects that may get displayed # on the canvas all_tasks.each do |object| create_or_get_item(object, true) object.each_event do |ev| create_or_get_item(ev, false) end end all_events.each { |ev| create_or_get_item(ev, true) } plans.each { |p| create_or_get_item(p, display_plan_bounding_boxes?) } update_visible_objects graphics.each do |object, item| item.visible = displayed?(object) end RelationsCanvasEventGenerator.priorities.clear event_priority = 0 plans.each do |p| flags = Hash.new(0) p.called_generators.each_with_index do |generator, priority| flags[generator] |= EVENT_CALLED end base_priority = p.called_generators.size p.emitted_events.each_with_index do |event, priority| generator = event.generator flags[generator] |= EVENT_EMITTED end p.failed_emissions.each do |generator, reason| flags[generator] = FAILED_EMISSION end flags.each_with_index do |(generator, generator_flags), priority| RelationsCanvasEventGenerator.priorities[generator] = priority if displayed?(generator) item = graphics[generator] item.brush, item.pen = RelationsCanvasEventGenerator.style( generator, generator_flags) end end end plans.each do |p| p.propagated_events.each do |_, sources, to, _| sources.each do |from| RelationsCanvasEventGenerator.priorities[from] ||= (event_priority += 1) RelationsCanvasEventGenerator.priorities[to] ||= (event_priority += 1) end end end [all_tasks, all_events, plans].each do |object_set| object_set.each do |object| graphics = self.graphics[object] if graphics.visible? object.display(self, graphics) end end end # Update arrow visibility arrows.each do |(from, to, rel), item| next if !@enabled_relations.include?(rel) item.visible = (displayed?(from) && displayed?(to)) end # Layout the graph layouts = plans.find_all { |p| p.root_plan? }. map do |p| dot = PlanDotLayout.new begin dot.layout(self, p, layout_options) dot rescue Exception => e puts "Failed to lay out the plan: #{e}" end end.compact layouts.each { |dot| dot.apply } # Display the signals signal_arrow_idx = -1 plans.each do |p| p.propagated_events.each_with_index do |(flag, sources, to), signal_arrow_idx| relation = if flag Roby::EventStructure::Forwarding else Roby::EventStructure::Signal end sources.each do |source_event| arrow = arrow(source_event.generator, to, relation, nil, EVENT_PROPAGATION_LAYER) propagation_style(arrow, flag) end end end arrows.each do |_, item| item.visible = true end @free_arrows = last_arrows.values free_arrows.each do |item| item.visible = false end last_arrows.clear true #rescue Exception => e # message = "<html>#{e.message.gsub('<', '<').gsub('>', '>')}<ul><li>#{e.backtrace.join("</li><li>")}</li></ul></html>" # Qt::MessageBox.critical nil, "Display failure", message end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 692 def update_relation_color(relation, color) relation_colors[relation] = color color = Qt::Color.new(color) pen = relation_pens[relation] = Qt::Pen.new(color) brush = relation_brushes[relation] = Qt::Brush.new(color) arrows.each do |(_, _, rel), arrow| if rel == relation arrow.pen = arrow.line.pen = pen arrow.brush = brush end end end
# File lib/roby/gui/relations_view/relations_canvas.rb, line 823 def update_visible_objects @visible_objects = Set.new # NOTE: we unconditionally add events that are propagated, as # #displayed?(obj) will filter out the ones whose task is hidden plans.each do |p| if display_plan_bounding_boxes? visible_objects << p end p.emitted_events.each do |event| visible_objects << event.generator end p.propagated_events.each do |_, sources, to, _| sources.each do |src| visible_objects << src.generator end visible_objects << to end end if display_policy == :explicit visible_objects.merge(selected_objects) elsif display_policy == :emitters || display_policy == :emitters_and_parents # Make sure that the event's tasks are added to # visible_objects as well visible_objects.dup.each do |obj| if parent = obj.display_parent visible_objects << parent end end end if display_policy == :emitters_and_parents while true new_visible_objects = Set.new visible_objects.group_by(&:plan).each do |plan, plan_objects| graphs = plan.each_task_relation_graph.find_all(&:root_relation?).map(&:reverse) new_visible_objects.merge(plan.compute_useful_tasks(plan_objects.to_set, graphs: graphs)) new_visible_objects.subtract(plan_objects.to_set) end break if new_visible_objects.empty? visible_objects.merge(new_visible_objects) end visible_objects.dup.each do |obj| if obj.kind_of?(Roby::Task) obj.each_relation do |rel| visible_objects.merge(obj.child_objects(rel)) end end end end if hide_finalized plans.each do |plan| all_finalized = plan.finalized_tasks | plan.finalized_events @visible_objects = visible_objects - all_finalized end end visible_objects.delete_if do |obj| filtered_out_label?(obj.display_name(self)) end end