Class: Roby::GUI::ChronicleWidget

Inherits:
Qt::AbstractScrollArea
  • Object
show all
Defined in:
lib/roby/gui/chronicle_widget.rb

Overview

Widget to display tasks on a chronicle (i.e. timelines)

Use ChronicleView when using a PlanRebuilderWidget

The following interactions are available:

* CTRL + wheel: change time scale
* ALT + wheel: horizontal scroll
* wheel: vertical scroll
* double-click: task info view

Defined Under Namespace

Classes: TaskLayout

Constant Summary collapse

SCALES =
[1, 2, 5, 10, 20, 30, 60, 90, 120, 300, 600, 1200, 1800, 3600].freeze
TIMELINE_GRAY_PEN =
Qt::Pen.new(Qt::Color.new("gray"))
TIMELINE_BLACK_PEN =
Qt::Pen.new(Qt::Color.new("black"))

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parent = nil) ⇒ ChronicleWidget

Returns a new instance of ChronicleWidget.



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/roby/gui/chronicle_widget.rb', line 222

def initialize(parent = nil)
    super(parent)

    @layout_cache = {}
    @messages_per_task = Hash.new { |h, k| h[k] = [] }
    @current_tasks = []
    @current_tasks_dirty = true
    self.time_scale = 10
    @task_height = 10
    @task_separation = 10
    @live_update_margin = 10
    @start_line = 0
    @all_tasks = Set.new
    @all_job_info = {}
    @scheduler_state = Schedulers::State.new
    @task_layout = []
    @sort_mode = :start_time
    @reverse_sort = false
    @show_mode = :all
    @show_future_events = true
    @live = true
    @track_current_time = true
    @horizontal_scroll_bar_down = false
    @display_point = viewport.size.width - live_update_margin

    viewport = Qt::Widget.new
    pal = Qt::Palette.new(viewport.palette)
    pal.setColor(Qt::Palette::Background, Qt::Color.new("white"))
    viewport.setAutoFillBackground(true)
    viewport.setPalette(pal)
    self.viewport = viewport

    horizontal_scroll_bar.connect(SIGNAL("sliderMoved(int)")) do
        value = horizontal_scroll_bar.value
        self.track_current_time = live? && (value == horizontal_scroll_bar.maximum)
        time = base_time + Float(value) * pixel_to_time
        update_display_time(time)
        emit timeChanged(time - base_time)
    end
    horizontal_scroll_bar.connect(SIGNAL("sliderPressed()")) do
        self.horizontal_scroll_bar_down = true
    end
    horizontal_scroll_bar.connect(SIGNAL("sliderReleased()")) do
        self.track_current_time = live? && (horizontal_scroll_bar.value == horizontal_scroll_bar.maximum)
        self.horizontal_scroll_bar_down = false
        update_scroll_ranges
    end
    vertical_scroll_bar.connect(SIGNAL("valueChanged(int)")) do
        value = vertical_scroll_bar.value
        if value < current_tasks.size
            self.start_line = value
            update
        end
    end
end

Instance Attribute Details

#all_job_infoObject

Job information about all known tasks

See Also:



97
98
99
# File 'lib/roby/gui/chronicle_widget.rb', line 97

def all_job_info
  @all_job_info
end

#all_tasksObject

All known tasks

See Also:



93
94
95
# File 'lib/roby/gui/chronicle_widget.rb', line 93

def all_tasks
  @all_tasks
end

#base_timeObject

The startup time



82
83
84
# File 'lib/roby/gui/chronicle_widget.rb', line 82

def base_time
  @base_time
end

#current_tasksObject (readonly)

The set of tasks that should currently be managed by the view.

It is updated in #update(), i.e. when the view gets something to display



108
109
110
# File 'lib/roby/gui/chronicle_widget.rb', line 108

def current_tasks
  @current_tasks
end

#current_timeObject

The system’s current time



80
81
82
# File 'lib/roby/gui/chronicle_widget.rb', line 80

def current_time
  @current_time
end

#display_pointObject

The point (in pixels) where the current display time should be located on the display



76
77
78
# File 'lib/roby/gui/chronicle_widget.rb', line 76

def display_point
  @display_point
end

#display_timeObject

The time that is currently at the middle of the view



78
79
80
# File 'lib/roby/gui/chronicle_widget.rb', line 78

def display_time
  @display_time
end

#displayed_time_range(Time,Time)? (readonly)

The range, in absolute time, currently visible in the view

Returns:

  • either said range, or nil if nothing has ever been displayed so far



465
466
467
# File 'lib/roby/gui/chronicle_widget.rb', line 465

def displayed_time_range
  @displayed_time_range
end

#filterObject

Inclusion filter on task names

If it contains a regular expression, only the task names that match the expression will be displayed



195
196
197
# File 'lib/roby/gui/chronicle_widget.rb', line 195

def filter
  @filter
end

#filter_outObject

Exclusion filter on task names

If it contains a regular expression, the task names that match the expression will not be displayed



208
209
210
# File 'lib/roby/gui/chronicle_widget.rb', line 208

def filter_out
  @filter_out
end

#layout_cacheHash<Task,TaskLayout> (readonly)

Per-task visual layout information

Returns:



158
159
160
# File 'lib/roby/gui/chronicle_widget.rb', line 158

def layout_cache
  @layout_cache
end

#live_update_marginObject (readonly)

How many pixels should there be between the ‘now’ line and the right side, in pixels



73
74
75
# File 'lib/roby/gui/chronicle_widget.rb', line 73

def live_update_margin
  @live_update_margin
end

#messages_per_taskObject (readonly)

Per-task messages to be displayed



161
162
163
# File 'lib/roby/gui/chronicle_widget.rb', line 161

def messages_per_task
  @messages_per_task
end

#pixel_to_timeObject (readonly)

Scale factor to convert seconds to pixels

pixel = time_to_pixel * time


69
70
71
# File 'lib/roby/gui/chronicle_widget.rb', line 69

def pixel_to_time
  @pixel_to_time
end

#position_to_taskObject (readonly)

An ordered set of [y, task], where y is the position in Y of the bottom of a task line and task the corresponding task object

It is updated on display



113
114
115
# File 'lib/roby/gui/chronicle_widget.rb', line 113

def position_to_task
  @position_to_task
end

#reverse_sort=(value) ⇒ Object (writeonly)

Whether the order defined by #sort_mode should be inverted



138
139
140
# File 'lib/roby/gui/chronicle_widget.rb', line 138

def reverse_sort=(value)
  @reverse_sort = value
end

#scheduler_stateSchedulers::State

Scheduler information

Returns:



101
102
103
# File 'lib/roby/gui/chronicle_widget.rb', line 101

def scheduler_state
  @scheduler_state
end

#show_modeObject

High-level filter on the list of shown tasks. Can either be :all, :running, :current. Defaults to :all

In :all mode, all tasks that are included in a plan in a certain point in time are displayed.

In :running mode, only the tasks that are running within the display time window are shown.

In :current mode, only the tasks that have emitted events within the display time window are shown

In :in_range mode, only the tasks that would display something within the display time window are shown



153
154
155
# File 'lib/roby/gui/chronicle_widget.rb', line 153

def show_mode
  @show_mode
end

#sort_modeObject

The current sorting mode. Can be :start_time or :last_event. Defaults to :start_time

In :start mode, the tasks are sorted by the time at which they started. In :last_event, by the time of the last event emitted before the current displayed time: it shows the last active tasks first)



121
122
123
# File 'lib/roby/gui/chronicle_widget.rb', line 121

def sort_mode
  @sort_mode
end

#start_lineObject

The index of the task that is currently at the top of the view. It is an index in #current_tasks



89
90
91
# File 'lib/roby/gui/chronicle_widget.rb', line 89

def start_line
  @start_line
end

#task_heightObject

The base height of a task line



84
85
86
# File 'lib/roby/gui/chronicle_widget.rb', line 84

def task_height
  @task_height
end

#task_layoutObject (readonly)

The task layout as computed in the last call to #paintEvent



103
104
105
# File 'lib/roby/gui/chronicle_widget.rb', line 103

def task_layout
  @task_layout
end

#task_separationObject

The separation, in pixels, between tasks



86
87
88
# File 'lib/roby/gui/chronicle_widget.rb', line 86

def task_separation
  @task_separation
end

#time_scaleObject

Internal representation of the desired time scale. Don’t use it directly, but use #time_to_pixel or #pixel_to_time



36
37
38
# File 'lib/roby/gui/chronicle_widget.rb', line 36

def time_scale
  @time_scale
end

#time_to_pixelObject (readonly)

Scale factor to convert seconds to pixels

pixels = time_to_pixel * time


64
65
66
# File 'lib/roby/gui/chronicle_widget.rb', line 64

def time_to_pixel
  @time_to_pixel
end

Instance Method Details

#add_tasks_info(tasks, job_info) ⇒ Object

Add information to the chronicle for the next display update

Parameters:

  • the set of tasks to display

  • from a placeholder task and the job task it represents



345
346
347
348
349
350
351
352
353
354
# File 'lib/roby/gui/chronicle_widget.rb', line 345

def add_tasks_info(tasks, job_info)
    tasks.each do |t|
        if base_time && t.addition_time < base_time
            update_base_time(t.addition_time)
        end
    end

    all_tasks.merge(tasks)
    all_job_info.merge!(job_info)
end

#clearObject



874
875
876
877
# File 'lib/roby/gui/chronicle_widget.rb', line 874

def clear
    all_tasks.clear
    all_job_info.clear
end

#clear_tasks_infoObject



313
314
315
316
317
# File 'lib/roby/gui/chronicle_widget.rb', line 313

def clear_tasks_info
    all_tasks.clear
    all_job_info.clear
    self.scheduler_state = Schedulers::State.new
end

#contents_heightObject



363
364
365
366
367
368
369
370
371
372
# File 'lib/roby/gui/chronicle_widget.rb', line 363

def contents_height
    update_current_tasks

    display_start, display_end = displayed_time_range
    fm = Qt::FontMetrics.new(font)
    height = current_tasks.inject(0) do |h, t|
        h + lay_out_task(fm, t).height(display_start, display_end)
    end
    height + current_tasks.size * task_separation + timeline_height
end

#current_tasks_dirty?Boolean

Returns:



471
472
473
# File 'lib/roby/gui/chronicle_widget.rb', line 471

def current_tasks_dirty?
    @current_tasks_dirty
end

#invalidate_current_tasksObject



467
468
469
# File 'lib/roby/gui/chronicle_widget.rb', line 467

def invalidate_current_tasks
    @current_tasks_dirty = true
end

#invalidate_layout_cacheObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Clears #layout_cache because parameters changed that require to recompute the task layouts

API:

  • private



167
168
169
# File 'lib/roby/gui/chronicle_widget.rb', line 167

def invalidate_layout_cache
    layout_cache.clear
end

#lay_out_task(fm, task) ⇒ Object



736
737
738
739
740
741
# File 'lib/roby/gui/chronicle_widget.rb', line 736

def lay_out_task(fm, task)
    layout = layout_cache[task] ||= TaskLayout.new(task, base_time, time_to_pixel, fm)
    layout.messages = messages_per_task.fetch(task, [])
    layout.update
    layout
end

#live=(flag) ⇒ Object



26
27
28
29
# File 'lib/roby/gui/chronicle_widget.rb', line 26

def live=(flag)
    @live = flag
    self.track_current_time = live? && (value == horizontal_scroll_bar.maximum)
end

#massage_slot_time_argument(time, default) ⇒ Object



550
551
552
553
554
555
556
557
558
559
# File 'lib/roby/gui/chronicle_widget.rb', line 550

def massage_slot_time_argument(time, default)
    # Convert from QDateTime to allow update() to be a slot
    if time.kind_of?(Qt::DateTime)
        Time.at(Float(time.toMSecsSinceEpoch) / 1000)
    elsif !time
        default
    else
        time
    end
end

#mouseDoubleClickEvent(event) ⇒ Object



879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
# File 'lib/roby/gui/chronicle_widget.rb', line 879

def mouseDoubleClickEvent(event)
    click_y = event.pos.y
    layout = task_layout.find { |layout| layout.top_y < click_y && layout.top_y + layout.height > click_y }
    if layout
        unless @info_view
            @info_view = ObjectInfoView.new
            Qt::Object.connect(@info_view, SIGNAL("selectedTime(QDateTime)"),
                               self, SIGNAL("selectedTime(QDateTime)"))
        end

        if @info_view.display(layout.task)
            @info_view.activate
        end
    end
    event.accept
end

#paint_tasks(painter, fm, layout, top_y) ⇒ Object



743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
# File 'lib/roby/gui/chronicle_widget.rb', line 743

def paint_tasks(painter, fm, layout, top_y)
    current_point  = Integer((current_time - base_time) * time_to_pixel)
    display_offset = Integer(display_point - (display_time - base_time) * time_to_pixel)
    display_start_time, display_end_time = displayed_time_range
    view_height = viewport.size.height

    text_height = fm.height
    text_ascent = fm.ascent
    text_descent = fm.descent

    update_current_tasks
    current_tasks[start_line..-1]&.each do |task|
        break if top_y > view_height

        task_layout = lay_out_task(fm, task)
        add_point, start_point, end_point, finalization_point =
            task_layout.add_point, task_layout.start_point, task_layout.end_point, task_layout.finalization_point
        state = task_layout.state
        task = task_layout.task
        event_height = task_layout.event_height

        task_line_height = event_height
        if events = task_layout.events_in_range(display_start_time, display_end_time)
            task_line_height += events.max_by { |_, _, y, _| y }[2]
        end
        if task_height > task_line_height
            task_line_height = task_height
        end

        # Paint the pending stage, i.e. before the task started
        top_task_line = top_y
        painter.brush = TASK_BRUSHES[:pending]
        painter.pen   = TASK_PENS[:pending]
        painter.drawRect(
            add_point + display_offset, top_task_line,
            (start_point || finalization_point || current_point) - add_point, task_line_height)

        if start_point
            painter.brush = TASK_BRUSHES[:running]
            painter.pen   = TASK_PENS[:running]
            painter.drawRect(
                start_point + display_offset, top_task_line,
                (end_point || current_point) - start_point, task_line_height)

            if state && state != :running
                # Final state is shown by "eating" a few pixels at the task
                painter.brush = TASK_BRUSHES[state]
                painter.pen   = TASK_PENS[state]
                painter.drawRect(
                    end_point - 2 + display_offset, top_task_line,
                    4, task_height)
            end
        end

        # Display the emitted events
        event_baseline = top_task_line + event_height / 2
        events&.each do |_, x, y, text|
            x += display_offset
            y += event_baseline
            painter.brush, painter.pen = EVENT_STYLES[EVENT_CONTROLABLE | EVENT_EMITTED]
            painter.drawEllipse(Qt::Point.new(x, y),
                                EVENT_CIRCLE_RADIUS, EVENT_CIRCLE_RADIUS)
            painter.pen = EVENT_NAME_PEN
            painter.drawText(Qt::Point.new(x + 2 * EVENT_CIRCLE_RADIUS, y), text)
        end

        # Add the title
        painter.pen = TASK_NAME_PEN
        title_baseline = top_task_line + task_line_height + text_ascent
        task_layout.title ||= task_timeline_title(task)
        painter.drawText(Qt::Point.new(0, title_baseline), task_layout.title)

        # And finally display associated messages
        messages_baseline = title_baseline + text_height
        painter.pen = TASK_MESSAGE_PEN
        task_layout.messages.each do |msg|
            messages_baseline += text_height
            painter.drawText(Qt::Point.new(TASK_MESSAGE_MARGIN, messages_baseline), msg)
        end

        top_y = messages_baseline + text_descent
    end
end

#paint_timeline(painter, fm) ⇒ Object



583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
# File 'lib/roby/gui/chronicle_widget.rb', line 583

def paint_timeline(painter, fm)
    text_height = fm.height
    window_size = viewport.size

    # Display the current cycle time
    central_label = Roby.format_time(display_time)
    central_label_width = fm.width(central_label)
    central_time_max = display_point + central_label_width / 2
    if central_time_max + 3 > window_size.width
        central_time_max = window_size.width - 3
    end
    central_time_min = central_time_max - central_label_width
    if central_time_min < 3
        central_time_min = 3
        central_time_max = central_time_min + central_label_width
    end
    painter.pen = TIMELINE_GRAY_PEN
    painter.drawText(central_time_min, text_height, central_label)
    painter.drawRect(central_time_min - 2, 0, central_label_width + 4, text_height + 2)

    # First, decide on the scale. We compute a "normal" text width
    # for the time labels, and check what would be a round time-step
    min_step_size = pixel_to_time * 1.5 * central_label_width
    step_size = SCALES.find do |scale|
        scale > min_step_size
    end
    step_size ||= SCALES.last
    # Now display the timeline itself. If a normal ruler collides
    # with the current time, just ignore it
    start_time, end_time = displayed_time_range
    painter.pen = TIMELINE_BLACK_PEN
    ruler_base_time = (start_time.to_f / step_size).floor * step_size
    ruler_base_x    = (ruler_base_time - start_time.to_f) * time_to_pixel
    step_count = ((end_time.to_f - ruler_base_time) / step_size).ceil
    step_count.times do |i|
        time = step_size * i + ruler_base_time
        pos  = step_size * i * time_to_pixel + ruler_base_x
        time_as_text = Roby.format_time(Time.at(time))
        time_as_text_width = fm.width(time_as_text)
        min_x = pos - time_as_text_width / 2
        max_x = pos + time_as_text_width / 2
        if central_time_min > max_x || central_time_max < min_x
            painter.drawText(min_x, text_height, time_as_text)
        end
        painter.drawLine(pos, text_height + fm.descent, pos, text_height + fm.descent + TIMELINE_RULER_LINE_LENGTH)
    end
end

#paintEvent(event) ⇒ Object



835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
# File 'lib/roby/gui/chronicle_widget.rb', line 835

def paintEvent(event)
    return unless display_time

    painter = Qt::Painter.new(viewport)
    font = painter.font
    font.point_size = 8
    painter.font = font

    fm = Qt::FontMetrics.new(font)
    update_current_tasks
    paint_timeline(painter, fm)
    paint_tasks(painter, fm, task_layout, timeline_height)

    # Draw the "zero" line
    painter.pen = TIMELINE_GRAY_PEN
    painter.drawLine(display_point, fm.height + 2, display_point, size.height)
ensure
    painter&.end
end

#remove_tasks(tasks) ⇒ Object



356
357
358
359
360
361
# File 'lib/roby/gui/chronicle_widget.rb', line 356

def remove_tasks(tasks)
    tasks.each do |t|
        all_tasks.delete(t)
        all_job_info.delete(t)
    end
end

#resizeEvent(event) ⇒ Object



440
441
442
443
444
445
446
447
448
449
# File 'lib/roby/gui/chronicle_widget.rb', line 440

def resizeEvent(event)
    if track_current_time?
        @display_point = event.size.width - live_update_margin
    elsif display_time && current_time
        update_display_point
    end
    update_displayed_time_range
    invalidate_current_tasks
    event.accept
end

#restrict_to_jobs=(set) ⇒ Object

Sets whether only the toplevel job tasks should be shown



185
186
187
188
189
# File 'lib/roby/gui/chronicle_widget.rb', line 185

def restrict_to_jobs=(set)
    @restrict_to_jobs = set
    setDisplayTime
    update
end

#restrict_to_jobs?Boolean

Returns true if only the action’s toplevel tasks are shown.

Returns:

  • true if only the action’s toplevel tasks are shown



182
# File 'lib/roby/gui/chronicle_widget.rb', line 182

attr_predicate :restrict_to_jobs?

#reverse_sort?Boolean

Whether the order defined by #sort_mode should be inverted

Returns:



133
134
135
# File 'lib/roby/gui/chronicle_widget.rb', line 133

def reverse_sort?
    !!@reverse_sort
end

#setCurrentTime(time = nil) ⇒ Object



572
573
574
575
576
577
578
579
# File 'lib/roby/gui/chronicle_widget.rb', line 572

def setCurrentTime(time = nil)
    time = massage_slot_time_argument(time, current_time)
    return unless time

    update_base_time(time) unless base_time
    update_current_time(time)
    update
end

#setDisplayTime(time = nil) ⇒ Object



561
562
563
564
565
566
567
568
569
# File 'lib/roby/gui/chronicle_widget.rb', line 561

def setDisplayTime(time = nil)
    time = massage_slot_time_argument(time, display_time)
    return unless time

    update_base_time(time) unless base_time
    update_current_time(time) unless current_time
    update_display_time(time)
    update
end

#task_timeline_title(task) ⇒ Object



855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
# File 'lib/roby/gui/chronicle_widget.rb', line 855

def task_timeline_title(task)
    text = task.to_s
    return text unless (job_task = all_job_info[task])

    job_text = ["[#{job_task.job_id}]"]
    job_text << job_task.job_name

    if job_task.respond_to?(:action_model)
        if job_task.action_model
            job_text << job_task.action_model.name.to_s
        end
        arg_s = (job_task.action_arguments || {})
                .map { |k, v| "#{k}: #{v}" }
        job_text << "(#{arg_s.join(', ')})"
        text = "#{job_text.join(' ')} / #{text}"
    end
    text
end

#timeline_heightObject



830
831
832
833
# File 'lib/roby/gui/chronicle_widget.rb', line 830

def timeline_height
    fm = Qt::FontMetrics.new(font)
    fm.height + fm.descent + TIMELINE_RULER_LINE_LENGTH
end

#update_base_time(time) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Update the time at the start of the chronicle

API:

  • private



388
389
390
391
392
# File 'lib/roby/gui/chronicle_widget.rb', line 388

def update_base_time(time)
    @base_time = time
    invalidate_current_tasks
    invalidate_layout_cache
end

#update_current_tasks(force: false) ⇒ Object



475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
# File 'lib/roby/gui/chronicle_widget.rb', line 475

def update_current_tasks(force: false)
    return if !force && !current_tasks_dirty?

    current_tasks = all_tasks.dup
    if restrict_to_jobs?
        current_tasks = all_job_info.keys.to_set
    end
    if filter
        current_tasks = current_tasks.find_all { |t| t.to_s =~ filter }
    end
    if filter_out
        current_tasks.delete_if { |t| t.to_s =~ filter_out }
    end
    started_tasks, pending_tasks = current_tasks.partition(&:start_time)

    if sort_mode == :last_event
        not_yet_started, started_tasks = started_tasks.partition { |t| t.start_time > display_time }
        current_tasks =
            started_tasks.sort_by do |t|
                last_event = nil
                t.history.each do |ev|
                    if ev.time < display_time
                        last_event = ev
                    else
                        break
                    end
                end
                last_event.time
            end
        current_tasks = current_tasks.reverse
        current_tasks.concat(not_yet_started.sort_by(&:start_time))
        if show_mode == :all
            current_tasks
                .concat(pending_tasks.sort_by(&:addition_time))
        end
    else
        current_tasks =
            (started_tasks + pending_tasks).sort_by { |t| t.start_time || t.addition_time }
    end

    start_time, end_time = displayed_time_range

    if start_time && (show_mode == :running || show_mode == :current)
        current_tasks = current_tasks.find_all do |t|
            (t.start_time && t.start_time < end_time) &&
                (!t.end_time || t.end_time > start_time)
        end

        if show_mode == :current
            current_tasks = current_tasks.find_all do |t|
                t.history.any? { |ev| ev.time > start_time && ev.time < end_time }
            end
        end
    end

    tasks_in_range, tasks_outside_range =
        current_tasks.partition do |t|
            (t.addition_time <= end_time) &&
                (!t.finalization_time || t.finalization_time >= start_time)
        end

    if reverse_sort?
        tasks_in_range = tasks_in_range.reverse
        tasks_outside_range = tasks_outside_range.reverse
    end

    @current_tasks_dirty = false
    if show_mode == :in_range
        @current_tasks = tasks_in_range
    else
        @current_tasks = tasks_in_range + tasks_outside_range
    end
    vertical_scroll_bar.setRange(0, current_tasks.size)
end

#update_current_time(time) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Update the time at the end of the chronicle

API:

  • private



396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/roby/gui/chronicle_widget.rb', line 396

def update_current_time(time)
    @current_time = time
    unless base_time
        update_base_time(time)
    end
    if !display_time || track_current_time?
        update_display_time(time)
    else
        update_scroll_ranges
        invalidate_current_tasks
    end
end

#update_display_pointObject



428
429
430
431
432
433
434
435
436
437
438
# File 'lib/roby/gui/chronicle_widget.rb', line 428

def update_display_point
    display_point = viewport.size.width - live_update_margin -
                    (current_time - display_time) * time_to_pixel
    display_point_min = viewport.size.width / 2
    if display_point < display_point_min
        display_point = display_point_min
    end
    @display_point = Integer(display_point)
    update_displayed_time_range
    invalidate_current_tasks
end

#update_display_time(time) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Update the currently displayed time

API:

  • private



411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
# File 'lib/roby/gui/chronicle_widget.rb', line 411

def update_display_time(time)
    @display_time = time
    unless base_time
        update_base_time(time)
    end

    _, end_time = displayed_time_range
    update_display_point

    unless horizontal_scroll_bar_down?
        update_scroll_ranges
        horizontal_scroll_bar.value = time_to_pixel * (display_time - base_time)
    end

    invalidate_current_tasks
end

#update_displayed_time_rangeObject



451
452
453
454
455
456
457
458
459
# File 'lib/roby/gui/chronicle_widget.rb', line 451

def update_displayed_time_range
    if display_time
        display_point = self.display_point
        window_width  = viewport.size.width
        start_time = display_time - display_point * pixel_to_time
        end_time   = start_time + window_width * pixel_to_time
        @displayed_time_range = [start_time, end_time]
    end
end

#update_scroll_rangesObject



898
899
900
901
902
903
904
905
906
907
908
909
# File 'lib/roby/gui/chronicle_widget.rb', line 898

def update_scroll_ranges
    vertical_scroll_bar.setRange(0, current_tasks.size - 1)
    line_max = current_tasks.empty? ? 0 : current_tasks.size - 1
    @start_line = [line_max, @start_line].compact.min
    return if horizontal_scroll_bar_down?

    if base_time && current_time && display_time
        horizontal_scroll_bar.value = time_to_pixel * (display_time - base_time)
        horizontal_scroll_bar.setRange(0, time_to_pixel * (current_time - base_time))
        horizontal_scroll_bar.setPageStep(size.width / 4)
    end
end

#update_time_range(start_time, current_time) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Updates the start and current time

API:

  • private



377
378
379
380
381
382
383
384
# File 'lib/roby/gui/chronicle_widget.rb', line 377

def update_time_range(start_time, current_time)
    if start_time
        update_base_time(start_time)
    end
    if current_time
        update_current_time(current_time)
    end
end

#wheelEvent(event) ⇒ Object

Event handler for wheel event



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/roby/gui/chronicle_widget.rb', line 283

def wheelEvent(event)
    if event.modifiers != Qt::ControlModifier
        # Don't let the user scroll with the mouse if vertical
        # scrolling is off
        if vertical_scroll_bar_policy == Qt::ScrollBarAlwaysOff
            event.ignore
            return
        else
            return super
        end
    end

    # See documentation of wheelEvent
    degrees = event.delta / 8.0
    num_steps = degrees / 15

    old = self.time_scale
    new = old + num_steps
    if new == 0
        if old > 0
            self.time_scale = -1
        else
            self.time_scale = 1
        end
    else
        self.time_scale = new
    end
    event.accept
end