module VizkitInfoViewer::Functions

Public Instance Methods

context_menu(menu, pos, object) click to toggle source

Opens given (context) menu at given position (in global coordinates). Submit the object the chosen action shall work on, i.e. a task or timer.

# File lib/vizkit/widgets/vizkit_info_viewer/vizkit_info_viewer.rb, line 157
def context_menu(menu, pos, object)
    action = menu.exec(pos)
    case action
        # Task actions
        when @action_stop_task then
            object.terminate!
        # Event timer actions
        when @action_cancel_timer then
            if timer_cancelled?(object)
                Vizkit.warn "You cannot cancel timers twice."
            else
                object.cancel
                @cancelled_timers << object
            end
        when @action_start_timer then
            if timer_cancelled?(object)
                @cancelled_timers.delete object
                object.start
            else
                Vizkit.warn "You cannot start timers twice."
            end
        when @action_delete_timer then
            object.cancel
            @cancelled_timers.delete object
        when nil
            # Ignore if no action is returned at all, e.g. if you discard the context menu.
        else Kernel.raise "Unsupported action"
    end
    
    update
end
init(parent = nil, update_frequency = 500, time_threshold = 1000) click to toggle source
# File lib/vizkit/widgets/vizkit_info_viewer/vizkit_info_viewer.rb, line 36
def init(parent = nil, update_frequency = 500, time_threshold = 1000)
    @event_loop = Orocos::Async.event_loop
    Kernel.raise "No event loop available!" unless @event_loop
    register_for_errors
    
    @update_frequency = update_frequency
    @time_threshold = time_threshold
    
    @thread_pool = @event_loop.thread_pool
    
    
    # References to the objects in the view. Saves them from garbage collection.
    @item_hash = Hash.new
    
    # Local keep-alive references of cancelled timers. 
    # Start them again to re-add them to the event loop.
    @cancelled_timers = []

    update_timer = Qt::Timer.new
    update_timer.connect(SIGNAL('timeout()')) do
        update
    end
    
    # Actions on tasks
    @action_stop_task = Qt::Action.new("Stop", self)
    
    # Actions on event timers
    @action_cancel_timer = Qt::Action.new("Cancel", self)
    @action_start_timer = Qt::Action.new("Start", self)
    @action_delete_timer = Qt::Action.new("Delete", self)
    
    # Context menus
    task_menu = Qt::Menu.new(self)
    treeWidget.set_context_menu_policy(Qt::CustomContextMenu)
    task_menu.add_action(@action_stop_task)
    
    event_timer_menu = Qt::Menu.new(self)
    event_tree.set_context_menu_policy(Qt::CustomContextMenu)
    event_timer_menu.add_action(@action_cancel_timer)
    event_timer_menu.add_action(@action_start_timer)
    event_timer_menu.add_action(@action_delete_timer)

    # Task tree
    task_column_headers = ["Task", "Time elapsed", "State"]
    treeWidget.set_column_count(task_column_headers.size)
    treeWidget.set_header_labels(task_column_headers)
    treeWidget.set_edit_triggers(Qt::AbstractItemView::NoEditTriggers)

    @active_task_item = Qt::TreeWidgetItem.new
    @active_task_item.set_text(0, "Active tasks")
    treeWidget.add_top_level_item(@active_task_item)

    @waiting_task_item = Qt::TreeWidgetItem.new
    @waiting_task_item.set_text(0, "Waiting tasks")
    treeWidget.add_top_level_item(@waiting_task_item)
    
    treeWidget.expand_to_depth 1
    
    # Event tree with timers
    event_column_headers = ["Timer","Period","State"]
    event_tree.set_column_count(event_column_headers.size)
    event_tree.set_header_labels(event_column_headers)
    event_tree.set_edit_triggers(Qt::AbstractItemView::NoEditTriggers)
    
    event_tree.connect(SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)')) do |item, col|
        if column_editable?(col)
            if start_edit(item)
                #event_tree.open_persistent_editor(item, col)
                event_tree.edit_item(item, col)
            end
        end
    end
    
    event_tree.connect(SIGNAL('itemChanged(QTreeWidgetItem*, int)')) do |item, col|
        # TODO The persistent editor does not get closed if the item's text does not change.
        if column_editable?(col)
            #timer = @item_hash[item]
            timer = item_data(item).to_ruby
            timer.period = item.text(col).to_f
            if end_edit(item)
                #event_tree.close_persistent_editor(item, col)
            end
        end
    end
    
    event_tree.item_delegate.connect(SIGNAL('closeEditor(QWidget*, QAbstractItemDelegate::EndEditHint)')) do |editor|
        end_edit
    end

    event_tree.expand_to_depth 1
    
    # Setup context menus on each view
    [treeWidget, event_tree].each do |view|
        view.connect(SIGNAL('customContextMenuRequested(const QPoint&)')) do |pos|
            item = view.item_at(pos)
            next unless item
            
            # Forbid context menu on top level items of tree view
            # XXX. Better check if there is a valid QVariant data object.
            next if view == treeWidget && item.parent.nil?
            
            object = item_data(item).to_ruby
            if object
                menu = case view
                    when treeWidget then task_menu
                    when event_tree then event_timer_menu
                    else Kernel.raise "Unsupported view"
                end
                context_menu(menu, view.viewport.mapToGlobal(pos), object)
            end
        end
    end
    update
    
    # Other initializations
    update_timer.start(@update_frequency)
    update_tree_view
end
update() click to toggle source
# File lib/vizkit/widgets/vizkit_info_viewer/vizkit_info_viewer.rb, line 189
def update
    ## Update task tree
    
    displayed_tasks = []
    dirty_tasks = []
    
    # Update currently displayed tasks
    [@active_task_item, @waiting_task_item].each do |item|
        ctr = 0
        while ctr < item.child_count do
            child = item.child(ctr)
            if @thread_pool.tasks.include? item_data(child).to_ruby
                update_task_item(child)
            else
                dirty_tasks << child
            end
            ctr = ctr + 1
        end
    end
    
    # Remove obsolete task items
    dirty_tasks.each do |child|
        @item_hash.delete(item_data(child).to_ruby)
        ret = child.parent.take_child(child.parent.index_of_child(child))
        Kernel.raise "Error during item deletion" unless ret
    end
    
    # Add new tasks to tree
    @thread_pool.tasks.each do |task|
        item = nil
        
        # Ignore tasks which have not yet run long enough but are already started.
        next if task.started? and task.time_elapsed <= @time_threshold / 1000
        
        if task.started?
            item = @active_task_item 
        else
            item = @waiting_task_item
        end
        
        child = task_data_item(task)
        item.add_child(child)

        # Make task accessible for context menu
        @item_hash[task] = child
    end
    
    ## Update event loop timer tree
    
    # Update current event timer items and remove obsolete items.
    dirty_timers = []
    
    # temp list
    displayed_timers = []

    root_item = event_tree.invisible_root_item
    ctr = 0
    
    while ctr < root_item.child_count do
        child = root_item.child(ctr)
        
        list = (@event_loop.timers + @cancelled_timers)
        timer = item_data(child).to_ruby
        if list.include? timer
            # child represents a non-obsolete timer. update.
            if @cancelled_timers.include? timer
                # display as cancelled
                update_timer_item(child, timer, true)
            else
                update_timer_item(child, timer, false)
            end
            # add item's timer object to temp list
            displayed_timers << item_data(child).to_ruby
        else
            # Mark obsolete event timer items for removal
            dirty_timers << child
        end
        ctr = ctr + 1
    end
    
    # Remove obsolete event timer items
    dirty_timers.each do |child|
        @item_hash.delete child
        ret = event_tree.take_top_level_item(event_tree.invisible_root_item.index_of_child(child))
        Kernel.raise "Error during item deletion" unless ret
    end
    
    # Check for new event timers
    (@event_loop.timers - displayed_timers).each do |t|
        next if t.single_shot?
        
        # Add item to tree
        child = event_timer_data_item(t, false)
        
        @item_hash[t] = child
        
        event_tree.add_top_level_item(child)
    end
    
    ## Update statistics view
    label_threads_total.set_text(@thread_pool.spawned.to_s)
    label_threads_waiting.set_text(@thread_pool.waiting.to_s)
    label_threads_backlog.set_text(@thread_pool.backlog.to_s)
    
    # Average run and wait times. Use UTC to avoid time zone handling.
    label_execution_time.set_text(Time.at(@thread_pool.avg_run_time).utc.strftime("%Hh %Mm %Ss"))
    label_waiting_time.set_text(Time.at(@thread_pool.avg_wait_time).utc.strftime("%Hh %Mm %Ss"))
    
    # Do this last! Minimize column width
    update_tree_view
end
update_tree_view() click to toggle source
# File lib/vizkit/widgets/vizkit_info_viewer/vizkit_info_viewer.rb, line 301
def update_tree_view
    treeWidget.column_count.times do |col|
        treeWidget.resize_column_to_contents col
    end
end