Class: Ruber::OutputWidget
- Includes:
- GuiStatesHandler
- Defined in:
- lib/ruber/output_widget.rb
Overview
Widget meant to be used as tool widget to display the output of a program. It is based on Qt Model/View classes and provides the following facitlities:
-
an easy way to display in different colors items with different meaning (for example, error message are displayed in a different color from output messages)
-
a centralized way in which the user can choose the colors for different types of items
-
a context menu with some standard actions, which can be enhanced with custom ones and is automatically (under certain conditions) shown to the user
-
autoscrolling (which means that whenever new text is added, the view scrolls so that the text becomes visible)
-
a centralized way for the user to turn on and off word wrapping (which can be ignored by widgets for which it doesn’t make sense)
-
a mechanism which allows the user to click on an entry containing a file name in the widget to open the file in the editor. The mechanism can be customized by plugins to be better tailored to their needs (and can also be turned off)
-
a model class, derived from
Qt::StandardItemModel
, which provides a couple of convenience methods to make text insertion even easier.
Note that OutputWidget is not (and doesn’t derive from) one of the View classes. Rather, it’s a normal @Qt::Widget@ which has the view as its only child. You can add other widgets to the OutputWidget as you would with any other widget: create the widget with the OutputWidget as parent and add it to the OutputWidget’s layout (which is a @Qt::GridLayout@ where the view is at position 0,0).
Note: this class defines two new roles (@OutputTypeRole@ and @IsTitleRole@), which correspond to @Qt::UserRole@ and @Qt::UserRole+1@. Therefore, if you need to define other custom roles, please have them start from @Ruber::OutputWidget::IsTitleRole+1@.
h3. Output types
The @output_type@ of an entry in the model can be set using the #set_output_type method. This has two effects: first, the item will be displayed using the color chosen by the user for that output type; second, the name of the output type will be stored in that item under the custom role @OutputTypeRole@.
There are several predefined output types: @message@, @message_good@, @message_bad@, @output@, @output1@, @output2@, @warning@, @warning1@, @warning2@, @error@, @error1@ and @error2@. The types ending with a number can be used when you need different types with similar meaning.
The @message@ type (and its variations) are meant to display messages which don’t come from the external program but from Ruber itself (for example, a message telling that the external problem exited successfully or exited with an error). Its good and bad version are meant to display messages with good news and bad news respectively (for example: “the program exited successfully” could be displayed using the @message_good@ type, while “the program crashed” could be displayed using the @message_bad@ type).
The @output@ type is meant to be used to show the text written by the external program on standard output, while the @error@ type is used to display the text written on standard error. If you can distinguish between warning and errors, you can use the @warning@ type for the latter.
The colors for the default output types are chosen by the user from the configuration dialog and are used whenever those output types are requested.
New output types (and their associated colors) can be make known to the output widget by using the @set_color_for@ method. There’s no need to remove the color, for example when the plugin is unloaded (indeed, there’s no way to do so).
h3. The context menu
This widget automatically creates a context menu containing three actions: Copy, Copy Selected and Clear. Copy and Copy Selected copy the text contained respectively in all the items and in the selected items to the clipboard. The clear action removes all the entries from the model.
You can add other actions to the menu by performing the following steps:
-
add an entry in the appropriate position of #action_list. Note that this is an instance of ActionList, so it provides the insert_after and insert_before methods which allow to easily put the actions in the correct place. #action_list contains the @object_name@ of the actions (and nil for the separators), not the action themselves
-
create the actions (setting their @object_name@ to the values inserted in #action_list) and put them into the #actions hash, using the @object_name@ as keys. Of course, you also need to define the appropriate slots and connect them to the actions’ signals.
Note that actions can only be added before the menu is first shown (usually, you do that in the widget’s constructor). The #about_to_fill_menu signal is emitted just before the menu is built: this is the last time you can add entries to it.
OutputWidget mixes in the GuiStatesHandler module, which means you can define states to enable and disable actions as usual. By default, two states are defined: @no_text@ and @no_selection@. As the names imply, the former is true when the model is empty and false when there’s at least one item; the second is true when no item is selected and false when there are selected items.
For the menu to be displayed automatically, the view should have a @context_menu_requested(QPoint)@ signal. The menu will be displayed in response to that signal, at the point given as argument. For convenience, there are three classes ListView, TreeView and TableView, derived respectively from @Qt::ListView@, @Qt::TreeView@ and @Qt::TableView@ which emit that signal from their @contextMenuEvent@ method. If you use one of these classes as view, the menu will work automatically.
h3. Autoscrolling
Whenever an item is added to the list, the view will be scrolled so that the added item is visible. Plugins which don’t want this feature can disable it by setting #auto_scroll to false. Note that auto scrolling won’t happen if an item is modified or removed
h3. Word wrapping
If the user has enabled word wrapping for output widgets in the config dialog (the @general/wrap_output@ option), word wrapping will be automatically enabled for all output widgets. If the user has disabled it, it will be disabled for all widgets.
Subclasses can avoid the automatic behaviour by setting the #ignore_word_wrap_option attribute to true and managing word wrap by themselves. This is mostly useful for output widgets for which word wrap is undesirable or meaningless.
h3. Opening files in the editor
Whenever the user activates an item, the text of the item is searched for a filename (and optionally for a line number). If it’s found, a new editor view is opened and the file is displayed in it. The editor can be an already existing editor or a new one created by splitting the current editor or in a new tab, according to the @general/tool_open_files@ option.
This process uses four methods:
-
#maybe_open_file:= the method connected to the view’s @activated(QModelIndex)@ signal. It starts the search for the filename and, if successful, opens the editor view =:
-
#find_filename_in_index:= performs the search of the filename. By default, it uses #find_filename_in_string, but subclasses can override it to change the behaviour=:
-
#find_filename_in_string:= the method used by default by #find_filename_in_index to find the filename.=:
-
#display_file:= opens the file in an editor. By default uses the @general/tool_open_files@ to decide how the editor should be created, but this behaviour can be overridden by subclasses.
If a relative filename is found, it’s considered relative to the directory contained in the #working_dir attribute.
h3. Model
The Model class behaves as a standard @Qt::StandardItemModel@, but provides two methods, insert and insert_lines which make easier adding items. You aren’t forced to use this model, however: if you want to use another class, pass it to the constructor.
Direct Known Subclasses
Defined Under Namespace
Classes: ActionList, ListView, Model, TableView, TreeView
Constant Summary collapse
- OutputTypeRole =
The role which contains a string with the output type of the index
Qt::UserRole
- IsTitleRole =
The role which contains whether an item is or not the title
OutputTypeRole + 1
Instance Attribute Summary collapse
-
#auto_scroll ⇒ Boolean
Whether auto scrolling should be enabled or not (default: true).
-
#ignore_word_wrap_option ⇒ Boolean
Whether or not word wrapping should respect the @general/wrap_output@ option (default: false).
-
#model ⇒ Qt::AbstractItemModel
readonly
The model used by the OutputWidget.
-
#skip_first_file_in_title ⇒ Boolean
Whether or not to #find_filename_in_index should skip the first file name in the title.
-
#view ⇒ Qt::AbstractItemView
readonly
The view used by the OutputWidget.
-
#working_dir ⇒ String
(also: #working_directory)
The directory used to resolve relative paths when opening a file (default nil).
Instance Method Summary collapse
-
#clear_output ⇒ nil
Removes all the entries from the model.
-
#has_title? ⇒ Boolean
Whether or not the output widget has a title.
-
#initialize(parent = nil, opts = {}) ⇒ OutputWidget
constructor
A new instance of OutputWidget.
-
#load_settings ⇒ nil
Loads the settings from the configuration file.
- #pinned_down? ⇒ Boolean
-
#scroll_to(idx) ⇒ nil
Scrolls the view so that the item corresponding the given index is visible.
-
#set_color_for(name, color) ⇒ nil
Associates a color with an output type.
-
#set_output_type(idx, type) ⇒ Symbol?
Changes the output type of a given index.
-
#title=(text) ⇒ nil
Gives a title to the widget.
-
#with_auto_scrolling(val) { ... } ⇒ Object
Executes a block while temporarily turning autoscrolling on or off.
Methods included from GuiStatesHandler
#change_state, included, #register_action_handler, #remove_action_handler_for, #state
Constructor Details
#initialize(parent = nil, opts = {}) ⇒ OutputWidget
Returns a new instance of OutputWidget.
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/ruber/output_widget.rb', line 275 def initialize parent = nil, opts = {} @ignore_word_wrap_option = false @working_dir = nil @skip_first_file_in_title = true @use_default_font = opts[:use_default_font] super parent initialize_states_handler (opts[:view] || :list) setup_model opts[:model] connect @view.selection_model, SIGNAL('selectionChanged(QItemSelection, QItemSelection)'), self, SLOT('selection_changed(QItemSelection, QItemSelection)') connect @view, SIGNAL('activated(QModelIndex)'), self, SLOT('maybe_open_file(QModelIndex)') @auto_scroll = true @colors = {} @action_list = ActionList.new @action_list << 'copy' << 'copy_selected' << nil << 'clear' @actions = {} connect @view, SIGNAL('context_menu_requested(QPoint)'), self, SLOT('show_menu(QPoint)') @menu = Qt::Menu.new self create_standard_actions change_state 'no_text', true change_state 'no_selection', true end |
Instance Attribute Details
#auto_scroll ⇒ Boolean
Returns whether auto scrolling should be enabled or not (default: true).
212 213 214 |
# File 'lib/ruber/output_widget.rb', line 212 def auto_scroll @auto_scroll end |
#ignore_word_wrap_option ⇒ Boolean
Returns whether or not word wrapping should respect the @general/wrap_output@ option (default: false).
217 218 219 |
# File 'lib/ruber/output_widget.rb', line 217 def ignore_word_wrap_option @ignore_word_wrap_option end |
#model ⇒ Qt::AbstractItemModel (readonly)
Returns the model used by the Ruber::OutputWidget.
251 252 253 |
# File 'lib/ruber/output_widget.rb', line 251 def model @model end |
#skip_first_file_in_title ⇒ Boolean
Returns whether or not to #find_filename_in_index should skip the first file name in the title.
230 231 232 |
# File 'lib/ruber/output_widget.rb', line 230 def skip_first_file_in_title @skip_first_file_in_title end |
#view ⇒ Qt::AbstractItemView (readonly)
Returns the view used by the OutputWidget.
256 257 258 |
# File 'lib/ruber/output_widget.rb', line 256 def view @view end |
#working_dir ⇒ String Also known as: working_directory
Returns the directory used to resolve relative paths when opening a file (default nil).
222 223 224 |
# File 'lib/ruber/output_widget.rb', line 222 def working_dir @working_dir end |
Instance Method Details
#clear_output ⇒ nil
Removes all the entries from the model
459 460 461 462 |
# File 'lib/ruber/output_widget.rb', line 459 def clear_output @model.remove_rows 0, @model.row_count nil end |
#has_title? ⇒ Boolean
Whether or not the output widget has a title
See #title= for what is meant here by title
426 427 428 |
# File 'lib/ruber/output_widget.rb', line 426 def has_title? @model.index(0,0).data(IsTitleRole).to_bool end |
#load_settings ⇒ nil
Loads the settings from the configuration file.
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'lib/ruber/output_widget.rb', line 435 def load_settings cfg = Ruber[:config] colors = [:message, :message_good, :message_bad, :output, :output1, :output2, :error, :error1, :error2, :warning, :warning1, :warning2] colors.each{|c| set_color_for c, cfg[:output_colors, c]} @model.row_count.times do |r| @model.column_count.times do |c| update_index_color @model.index(r, c) end end @view.font = cfg[:general, :output_font] unless @use_default_font unless @ignore_word_wrap_option # Not all the views support word wrapping begin @view.word_wrap = cfg[:general, :wrap_output] rescue NoMethodError end end nil end |
#pinned_down? ⇒ Boolean
464 465 466 |
# File 'lib/ruber/output_widget.rb', line 464 def pinned_down? @pin_button.checked? end |
#scroll_to(idx) ⇒ nil
Scrolls the view so that the item corresponding the given index is visible
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/ruber/output_widget.rb', line 329 def scroll_to idx case idx when Numeric rc = @model.row_count if idx >= rc then idx = rc -1 elsif idx < 0 and idx.abs < rc then idx = rc + idx elsif idx < 0 then idx = 0 end mod_idx = @model.index idx, 0 @view.scroll_to mod_idx, Qt::AbstractItemView::PositionAtBottom when Qt::ModelIndex idx = @model.index(@model.row_count - 1, 0) unless idx.valid? @view.scroll_to idx, Qt::AbstractItemView::PositionAtBottom when nil @view.scroll_to @model.index(@model.row_count - 1, 0), Qt::AbstractItemView::PositionAtBottom end nil end |
#set_color_for(name, color) ⇒ nil
Associates a color with an output type
If a color had already been associated with the given output type, it’ll be overwritten.
This method is useful to define new output types.
314 315 316 317 |
# File 'lib/ruber/output_widget.rb', line 314 def set_color_for name, color @colors[name] = color nil end |
#set_output_type(idx, type) ⇒ Symbol?
Changes the output type of a given index
If a color has been associated with that output type, the foreground role and the output type role of that index are updated accordingly.
If no color has been associated with the output type, nothing is done
360 361 362 363 364 365 366 367 |
# File 'lib/ruber/output_widget.rb', line 360 def set_output_type idx, type color = @colors[type] if color @model.set_data idx, Qt::Variant.from_value(color), Qt::ForegroundRole @model.set_data idx, Qt::Variant.new(type.to_s), OutputTypeRole type end end |
#title=(text) ⇒ nil
Gives a title to the widget.
A title is a toplevel entry at position 0, 0 with output type @:message@ and has the @IsTitleRole@ set to true. Of course, there can be only one item which is a title.
If the item in position 0, 0 is not a title, a new row with title role and the given text is inserted.
If the item in position 0,0 is a title, then its display role is replaced with the given text.
Usually, the title is created when the external program is started and changed later if needed.
405 406 407 408 409 410 411 412 413 414 415 416 417 418 |
# File 'lib/ruber/output_widget.rb', line 405 def title= text idx = @model.index 0, 0 if idx.data(IsTitleRole).to_bool @model.set_data idx, Qt::Variant.new(text) else @model.insert_column 0 if @model.column_count == 0 @model.insert_row 0 idx = @model.index 0, 0 @model.set_data idx, Qt::Variant.new(text) @model.set_data idx, Qt::Variant.new(true), IsTitleRole end set_output_type idx, :message nil end |
#with_auto_scrolling(val) { ... } ⇒ Object
Executes a block while temporarily turning autoscrolling on or off
After the block has been executed, autoscrolling returns to the original state.
378 379 380 381 382 383 384 |
# File 'lib/ruber/output_widget.rb', line 378 def with_auto_scrolling val old = @auto_scroll @auto_scroll = val begin yield ensure @auto_scroll = old end end |