Class: Syskit::GUI::IDE

Inherits:
Qt::Widget
  • Object
show all
Defined in:
lib/syskit/gui/ide.rb

Overview

The main Syskit IDE window

Defined Under Namespace

Classes: Picker

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parent = nil, runtime_only: false, host: 'localhost', port: Roby::Interface::DEFAULT_PORT, runtime: nil, tests: false, robot_name: 'default') ⇒ IDE

Returns a new instance of IDE



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/syskit/gui/ide.rb', line 21

def initialize(parent = nil,
    runtime_only: false,
    host: 'localhost', port: Roby::Interface::DEFAULT_PORT, runtime: nil, tests: false, robot_name: 'default')
    super(parent)

    @layout = Qt::VBoxLayout.new(self)
    @tab_widget = Qt::TabWidget.new(self)
    @layout.add_widget tab_widget
    @robot_name = robot_name

    unless runtime_only
        @testing = Testing.new
        @model_browser = ModelBrowser.new
        @btn_reload_models = Qt::PushButton.new("Reload Models", self)
        @btn_add = Qt::PushButton.new("Add", self)
        btn_add_menu = Qt::Menu.new
        btn_add_menu.add_action 'OroGen Project'
        btn_add_menu.add_action 'OroGen Type'
        btn_add_menu.add_action 'Model File'
        btn_add_menu.connect(SIGNAL('triggered(QAction*)')) do |action|
            case action.text
            when "OroGen Project"
                add_orogen_project
            when "OroGen Type"
                add_orogen_type
            when "Model File"
                add_model_file
            end
        end
        @btn_add.menu = btn_add_menu

        connect(model_browser, SIGNAL('fileOpenClicked(const QUrl&)'),
                self, SLOT('fileOpenClicked(const QUrl&)'))
        connect(testing, SIGNAL('fileOpenClicked(const QUrl&)'),
                self, SLOT('fileOpenClicked(const QUrl&)'))

        browse_container = Qt::Widget.new
        browse_container_layout = Qt::VBoxLayout.new(browse_container)
        status_bar = testing.create_status_bar_ui
        status_bar.insert_widget(0, @btn_reload_models)
        status_bar.insert_widget(1, @btn_add)
        browse_container_layout.add_layout(status_bar)
        browse_container_layout.add_widget(model_browser)
        tab_widget.add_tab browse_container, "Browse"
        tab_widget.add_tab testing, "Testing"

        btn_reload_models.connect(SIGNAL('clicked()')) do
            reload_models
        end
        model_browser.model_selector.filter_box.set_focus(Qt::OtherFocusReason)
    end

    if runtime != false
        syskit = Roby::Interface::Async::Interface.new(host, port: port)
        create_runtime_state_ui(syskit)
        runtime_idx = tab_widget.add_tab runtime_state, "Runtime"
        connect(@runtime_state, SIGNAL('fileOpenClicked(const QUrl&)'),
                self, SLOT('fileOpenClicked(const QUrl&)'))
    end

    if runtime
        tab_widget.current_index = runtime_idx
    end

    if tests
        testing.start
    end
end

Instance Attribute Details

#btn_reload_modelsObject (readonly)

Returns the value of attribute btn_reload_models



14
15
16
# File 'lib/syskit/gui/ide.rb', line 14

def btn_reload_models
  @btn_reload_models
end

#connection_stateObject (readonly)

Returns the value of attribute connection_state



18
19
20
# File 'lib/syskit/gui/ide.rb', line 18

def connection_state
  @connection_state
end

#layoutObject (readonly)

Returns the value of attribute layout



13
14
15
# File 'lib/syskit/gui/ide.rb', line 13

def layout
  @layout
end

#model_browserObject (readonly)

Returns the value of attribute model_browser



16
17
18
# File 'lib/syskit/gui/ide.rb', line 16

def model_browser
  @model_browser
end

#runtime_stateObject (readonly)

Returns the value of attribute runtime_state



17
18
19
# File 'lib/syskit/gui/ide.rb', line 17

def runtime_state
  @runtime_state
end

#tab_widgetObject (readonly)

Returns the value of attribute tab_widget



15
16
17
# File 'lib/syskit/gui/ide.rb', line 15

def tab_widget
  @tab_widget
end

#testingObject (readonly)

Returns the value of attribute testing



19
20
21
# File 'lib/syskit/gui/ide.rb', line 19

def testing
  @testing
end

Instance Method Details

#add_model_fileObject



214
215
216
217
218
219
220
221
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
# File 'lib/syskit/gui/ide.rb', line 214

def add_model_file
    models_dir = File.join(Roby.app.app_dir, 'models')
    initial_dir =
        if File.directory?(models_dir)
            models_dir
        else
            Roby.app.app_dir
        end

    existing_models = Roby.app.root_models.
        flat_map { |root| root.each_submodel.to_a }.
        to_set

    files = Qt::FileDialog.getOpenFileNames(
        self, "Pick model file(s) to add", initial_dir)
    files.each do |path|
        Roby.app.require(path)
        Roby.app.additional_model_files << path
        @model_browser.update_exceptions
        @model_browser.reload
    end

    new_models = []
    Roby.app.root_models.each do |root|
        root.each_submodel do |m|
            new_models << m unless existing_models.include?(m)
        end
    end

    orogen_based, other = new_models.partition do |m|
        m <= Syskit::TaskContext && !(m <= Syskit::RubyTaskContext)
    end
    if (new_model = other.first || orogen_based.first)
        @model_browser.select_by_model(new_model)
    end
end

#add_orogen_projectObject



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/syskit/gui/ide.rb', line 171

def add_orogen_project
    loader = Roby.app.default_pkgconfig_loader
    model_names = loader.each_available_task_model_name.to_a
    syskit2orogen = model_names.
        each_with_object(Hash.new) do |(orogen_name, project_name), result|
            unless loader.has_loaded_project?(project_name)
                syskit_path = ['OroGen', *orogen_name.split('::')]
                syskit_name = syskit_path.join(".")
                result[syskit_name] = [syskit_path, project_name]
            end
        end

    if (selected = Picker.select(self, syskit2orogen.keys))
        syskit_path, project_name = syskit2orogen[selected]
        Roby.app.using_task_library(project_name)
        Roby.app.extra_required_task_libraries << project_name
        @model_browser.reload
        @model_browser.select_by_path(*syskit_path)
    end
end

#add_orogen_typeObject



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/syskit/gui/ide.rb', line 192

def add_orogen_type
    loader = Roby.app.default_pkgconfig_loader
    syskit2orogen = loader.each_available_type_name.
        each_with_object(Hash.new) do |(type_name, typekit_name, _), result|
            next if type_name.end_with?("_m")
            next if type_name =~ /\[/
            unless loader.has_loaded_typekit?(typekit_name)
                syskit_path = ["Types", *Typelib.split_typename(type_name)]
                syskit_name = syskit_path.join(".")
                result[syskit_name] = [syskit_path, typekit_name]
            end
        end

    if (selected = Picker.select(self, syskit2orogen.keys))
        syskit_path, typekit_name = syskit2orogen[selected]
        Roby.app.extra_required_typekits << typekit_name
        Roby.app.import_types_from(typekit_name)
        @model_browser.reload
        @model_browser.select_by_path(*syskit_path)
    end
end

#create_runtime_state_ui(syskit) ⇒ Object



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/syskit/gui/ide.rb', line 251

def create_runtime_state_ui(syskit)
    @runtime_state = RuntimeState.new(syskit: syskit, robot_name: @robot_name)
    connect(runtime_state, SIGNAL('fileOpenClicked(const QUrl&)'),
            self, SLOT('fileOpenClicked(const QUrl&)'))
    @connection_state = GlobalStateLabel.new(
        actions: runtime_state.global_actions.values,
        name: runtime_state.remote_name)
    @connection_state.connect(SIGNAL('clicked(QPoint)')) do |global_pos|
        @connection_state.app_state_menu(global_pos)
    end

    tab_widget.set_corner_widget(connection_state, Qt::TopLeftCorner)
    runtime_state.on_connection_state_changed do |state|
        connection_state.update_state state
    end
    runtime_state.on_progress do |message|
        state = connection_state.current_state.to_s
        connection_state.update_text("%s - %s" % [state, message])
    end
end

#fileOpenClicked(url) ⇒ Object



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/syskit/gui/ide.rb', line 305

def fileOpenClicked(url)
    edit_cmd = global_settings.value('Main/cmdline', Qt::Variant.new)
    if edit_cmd.null?
        Qt::MessageBox.warning(self, "Edit File", "No editor configured to open file #{url.to_string}. Edit #{global_settings.file_name} and add a cmdline= line in the [Main] section there. The %PATH and %LINENO placeholders will be replaced (if present) by the path and line number that should be edited")
    else
        edit_cmd = edit_cmd.to_string.
            gsub("%FILEPATH", url.to_local_file).
            gsub("%LINENO", url.query_item_value('lineno') || '0')

        edit_cmd = Shellwords.shellsplit(edit_cmd)
        stdin, stdout, stderr, wait_thr = Open3.popen3(*edit_cmd)
        status = wait_thr.value
        if !status.success?
            Qt::MessageBox.warning(self, "Edit File", "Runninga \"#{edit_cmd.join('" "')}\" failed\n\nProcess reported: #{stderr.read}")
        end
    end
end

#global_settingsObject



272
273
274
# File 'lib/syskit/gui/ide.rb', line 272

def global_settings
    @global_settings ||= Qt::Settings.new('syskit')
end

#reload_modelsObject



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/syskit/gui/ide.rb', line 90

def reload_models
    if @runtime_state && @runtime_state.current_state != 'UNREACHABLE'
        Qt::MessageBox.warning(
            self, "Cannot Reload while running",
            "Cannot reload while an app is running, quit the app first")
        return
    end

    model_browser.registered_exceptions.clear
    Roby.app.clear_exceptions
    Roby.app.cleanup
    Roby.app.clear_models
    Roby.app.clear_config
    Roby.app.setup
    # HACK: reload_models calls Orocos.clear, which actually
    # HACK: de-initializes Orocos. Overall, this isn't a problem
    # HACK: on the Syskit side as one is not supposed to reload
    # HACK: the models while the app is setup (setup being
    # HACK: what calls Orocos.initialize). However, the IDE also
    # HACK: has a task inspector, which also needs
    # HACK: Orocos.initialize, so the IDE *does* call initialize
    # HACK: explicitely
    @runtime_state.reset if @runtime_state
    model_browser.update_exceptions
    model_browser.reload
    testing.reloaded
end

#restore_from_settings(settings = self.settings) ⇒ Object



280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/syskit/gui/ide.rb', line 280

def restore_from_settings(settings = self.settings)
    self.size = settings.value("MainWindow/size", Qt::Variant.new(Qt::Size.new(800, 600))).to_size
    %w{model_browser testing}.each do |child_object_name|
        next unless send(child_object_name)
        settings.begin_group(child_object_name)
        begin
            send(child_object_name).restore_from_settings(settings)
        ensure
            settings.end_group
        end
    end
end

#save_to_settings(settings = self.settings) ⇒ Object



292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/syskit/gui/ide.rb', line 292

def save_to_settings(settings = self.settings)
    settings.set_value("MainWindow/size", Qt::Variant.new(size))
    %w{model_browser testing}.each do |child_object_name|
        next unless send(child_object_name)
        settings.begin_group(child_object_name)
        begin
            send(child_object_name).save_to_settings(settings)
        ensure
            settings.end_group
        end
    end
end

#settingsObject



276
277
278
# File 'lib/syskit/gui/ide.rb', line 276

def settings
    @settings ||= Qt::Settings.new('syskit', 'ide')
end