Class: Cosmos::ProgressDialog

Inherits:
Qt::Dialog show all
Defined in:
lib/cosmos/gui/dialogs/progress_dialog.rb,
lib/cosmos/tools/table_manager/table_manager.rb

Overview

ProgressDialog class

The QT GUI model is to use slots and signals to connect GUI elements together. This is especially important with multithreaded applications because only the main thread can update the GUI and other attempts will cause crashes.

It seems like we should be able to do something like the following:

progress_dialog = ProgressDialog.new(self, 'Progress')
thread = MyRubyThreadWorker.new
connect(thread, SIGNAL('finished(int)'), progress_dialog, SLOT('done(int)'), Qt::QueuedConnection)
thread.start
progress_dialog.exec

This is creating a thread (but not starting the thread) and then connecting the thread classes ‘finished(int)’ signal to the progress_dialog classes ‘done(int)’ signal. It then starts the thread and calls exec on the dialog which makes it a modal dialog that starts its internal QEventLoop going. This allows the GUI to remain updating (so we can click cancel) while the thread runs. Then when the thread finished it should emit its ‘finished(int)’ signal which closes the dialog.

This all SHOULD work but it tends to randomly crash on Ruby 1.8.6. HOWEVER, it appears to work well on 1.9.1. We should consider going to this model in COSMOS 2.0 as it avoids the hacky

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Qt::Dialog

#exec

Constructor Details

#initialize(parent, title, width = 500, height = 300, show_overall = true, show_step = true, show_text = true, show_done = true, show_cancel = true) ⇒ ProgressDialog

Returns a new instance of ProgressDialog.



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
89
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 52

def initialize(parent, title, width = 500, height = 300, show_overall = true, show_step = true, show_text = true, show_done = true, show_cancel = true)
  if show_cancel
    super(parent)
  else
    super(parent, Qt::CustomizeWindowHint | Qt::WindowTitleHint)
  end
  setWindowTitle(title)
  setMinimumWidth(width)
  setMinimumHeight(height)
  @overall_bar = nil
  @step_bar = nil
  @progress_text = nil
  @done_button = nil
  @cancel_button = nil
  @@canceled = false
  @complete = false

  @overall = Qt::HBoxLayout.new
  @overall_bar = nil
  if show_overall
    # Create Overall progress bar
    @overall_bar = Qt::ProgressBar.new
    @overall_bar.setMaximum(100)
    @overall_label = Qt::Label.new("Overall Progress: ")
    @overall.addWidget(@overall_label)
    @overall.addWidget(@overall_bar)
  end

  @step = Qt::HBoxLayout.new
  @step_bar = nil
  if show_step
    # Create Step progress bar
    @step_bar = Qt::ProgressBar.new
    @step_bar.setMaximum(100)
    @step_label = Qt::Label.new("Step Progress: ")
    @step.addWidget(@step_label)
    @step.addWidget(@step_bar)
  end

  @progress_text = nil
  if show_text
    # Create Progress Text Notifications
    @progress_text = Qt::PlainTextEdit.new
    @progress_text.setReadOnly(true)
    @progress_text.setMaximumBlockCount(100)
  end

  @button_layout = Qt::HBoxLayout.new
  @done_button = nil
  if show_done
    # Create Done Button
    @done_button = Qt::PushButton.new('Done')
    @done_button.connect(SIGNAL('clicked()')) { self.close_done }
    @done_button.setEnabled(false)
    @button_layout.addWidget(@done_button)
  end

  @cancel_button = nil
  if show_cancel
    # Create Cancel Button
    @cancel_button = Qt::PushButton.new('Cancel')
    @cancel_button.connect(SIGNAL('clicked()')) { self.close_cancel }
    @cancel_button.setEnabled(false)
    @button_layout.addWidget(@cancel_button)
  end

  @top_layout = Qt::VBoxLayout.new
  @top_layout.addLayout(@overall) if show_overall
  @top_layout.addLayout(@step) if show_step
  @top_layout.addWidget(@progress_text) if show_text
  @top_layout.addLayout(@button_layout) if show_done or show_cancel

  setLayout(@top_layout)

  @thread = nil
  @cancel_callback = nil
  @overall_progress = 0
  @step_progress = 0
end

Instance Attribute Details

#cancel_callbackObject

Returns the value of attribute cancel_callback.



44
45
46
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 44

def cancel_callback
  @cancel_callback
end

#completeObject



196
197
198
199
200
201
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 196

def complete
  Qt.execute_in_main_thread(true) do
    @done_button.setEnabled(true) if @done_button
    @cancel_button.setEnabled(true) if @cancel_button
  end
end

#threadObject

Returns the value of attribute thread.



45
46
47
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 45

def thread
  @thread
end

Class Method Details

.canceled?Boolean

Returns:

  • (Boolean)


132
133
134
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 132

def self.canceled?
  @@canceled
end

.execute(parent, title, width = 500, height = 300, show_overall = true, show_step = true, show_text = true, show_done = true, show_cancel = true) ⇒ Object



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
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 239

def self.execute(parent, title, width = 500, height = 300, show_overall = true, show_step = true, show_text = true, show_done = true, show_cancel = true)
  # Create a non-modal dialog by default
  dialog = ProgressDialog.new(parent, title, width, height, show_overall, show_step, show_text, show_done, show_cancel)
  dialog.setModal(true)
  dialog.raise

  dialog.thread = Thread.new do
    dialog.thread = Thread.current
    dialog.complete = false

    begin
      yield dialog
    rescue Exception => error
      Qt.execute_in_main_thread(true) do
        # If something bad happened during the yield we'll show the error but not exit the application
        # Once the block has completed we hide and dispose the dialog to allow the main application to take over
        dialog.hide
        ExceptionDialog.new(parent, error, "Error During Progress", false)
      end
    end

    dialog.thread = nil
    dialog.complete = true
  end
  dialog.exec
  Cosmos.kill_thread(dialog, dialog.thread)
  dialog.thread = nil
  dialog.complete = true

  # Need to make sure all Qt.execute_in_main_thread() have completed before disposing or
  # we will segfault
  Qt::RubyThreadFix.queue.pop.call until Qt::RubyThreadFix.queue.empty?

  dialog.dispose
end

Instance Method Details

#append_text(string) ⇒ Object



185
186
187
188
189
190
191
192
193
194
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 185

def append_text(string)
  unless @complete
    Qt.execute_in_main_thread(false) do
      if @progress_text
        @progress_text.appendPlainText(string)
        @progress_text.ensureCursorVisible
      end
    end
  end
end

#canceled?Boolean

Returns:

  • (Boolean)


136
137
138
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 136

def canceled?
  @@canceled
end

#close_cancelObject



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 157

def close_cancel
  Qt.execute_in_main_thread(true) do
    kill_thread = true
    if @cancel_callback
      continue_cancel, kill_thread = @cancel_callback.call(self)
      return unless continue_cancel
    end
    @@canceled = true
    Thread.new do
      if @thread
        Cosmos.kill_thread(self, @thread) if kill_thread
        @thread.join
      end
      @thread = nil

      close_done()
    end
  end
end

#close_doneObject



150
151
152
153
154
155
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 150

def close_done
  Qt.execute_in_main_thread(true) do
    @complete = true
    self.done(0) unless self.disposed?
  end
end

#complete?Boolean

Returns:

  • (Boolean)


140
141
142
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 140

def complete?
  return @complete
end

#enable_cancel_buttonObject



144
145
146
147
148
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 144

def enable_cancel_button
  Qt.execute_in_main_thread(true) do
    @cancel_button.setEnabled(true) if @cancel_button
  end
end

#get_overall_progressObject



231
232
233
234
235
236
237
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 231

def get_overall_progress
  result = nil
  Qt.execute_in_main_thread(true) do
    result = (@overall_bar.value.to_f / 100.0) if @overall_bar
  end
  return result
end

#get_step_progressObject



223
224
225
226
227
228
229
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 223

def get_step_progress
  result = nil
  Qt.execute_in_main_thread(true) do
    result = (@step_bar.value.to_f / 100.0) if @step_bar
  end
  return result
end

#graceful_killObject



177
178
179
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 177

def graceful_kill
  # Do nothing - just to remove warning
end

#progress=(increment) ⇒ Object



131
132
133
# File 'lib/cosmos/tools/table_manager/table_manager.rb', line 131

def progress=(increment)
  set_overall_progress(increment / 100.0)
end

#set_overall_progress(value) ⇒ Object



213
214
215
216
217
218
219
220
221
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 213

def set_overall_progress (value)
  progress_int = (value * 100).to_i
  if !@complete and @overall_progress != progress_int
    @overall_progress = progress_int
    Qt.execute_in_main_thread(false) do
      @overall_bar.setValue(progress_int) if @overall_bar
    end
  end
end

#set_step_progress(value) ⇒ Object



203
204
205
206
207
208
209
210
211
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 203

def set_step_progress (value)
  progress_int = (value * 100).to_i
  if !@complete and @step_progress != progress_int
    @step_progress = progress_int
    Qt.execute_in_main_thread(false) do
      @step_bar.setValue(progress_int) if @step_bar
    end
  end
end

#set_text_font(font) ⇒ Object



181
182
183
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 181

def set_text_font(font)
  @progress_text.setFont(font)
end