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

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
# 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(10000)
  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
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



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

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)


130
131
132
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 130

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



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

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



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

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)


134
135
136
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 134

def canceled?
  @@canceled
end

#close_cancelObject



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

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



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

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

#complete?Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 138

def complete?
  return @complete
end

#enable_cancel_buttonObject



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

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

#get_overall_progressObject



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

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



217
218
219
220
221
222
223
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 217

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



175
176
177
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 175

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



209
210
211
212
213
214
215
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 209

def set_overall_progress (value)
  unless @complete
    Qt.execute_in_main_thread(false) do
      @overall_bar.setValue(value * 100.0) if @overall_bar
    end
  end
end

#set_step_progress(value) ⇒ Object



201
202
203
204
205
206
207
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 201

def set_step_progress (value)
  unless @complete
    Qt.execute_in_main_thread(false) do
      @step_bar.setValue(value * 100.0) if @step_bar
    end
  end
end

#set_text_font(font) ⇒ Object



179
180
181
# File 'lib/cosmos/gui/dialogs/progress_dialog.rb', line 179

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