From 3e37c0bf7b40d9ecf9a098a71e6e4559e464844d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 20 Sep 2017 01:22:25 +0200 Subject: [PATCH] First version of XOR progress visualization. --- src/ext/ext/extXORToolDialog.cc | 359 ++++++++++++++++++++++++++++++- src/lay/lay/layMainWindow.cc | 77 +++++++ src/lay/lay/layMainWindow.h | 24 +++ src/lay/lay/layProgress.cc | 16 +- src/lay/lay/layProgress.h | 4 + src/lay/lay/layProgressWidget.cc | 53 ++++- src/lay/lay/layProgressWidget.h | 7 + src/tl/tl/tlProgress.h | 15 ++ 8 files changed, 535 insertions(+), 20 deletions(-) diff --git a/src/ext/ext/extXORToolDialog.cc b/src/ext/ext/extXORToolDialog.cc index 32a2b7017..84a038bd8 100644 --- a/src/ext/ext/extXORToolDialog.cc +++ b/src/ext/ext/extXORToolDialog.cc @@ -42,6 +42,8 @@ #include #include +#include +#include namespace ext { @@ -157,6 +159,297 @@ struct RegionModeConverter } }; +struct CounterCompare +{ + typedef std::pair value_type; + bool operator() (const value_type &a, const value_type &b) const + { + if (a.second != b.second) { + return a.second > b.second; + } + return a.first < b.first; + } +}; + +const size_t missing_in_a = std::numeric_limits::max (); +const size_t missing_in_b = std::numeric_limits::max () - 1; + +class XORProgressWidget + : public QWidget +{ +public: + XORProgressWidget () + : QWidget (0) + { + m_pixmap_size = 24; + m_max_lines = 5; + m_spacing = 2; + + QFontMetrics fm (font ()); + m_line_height = std::max (fm.height (), m_pixmap_size + 4); + m_font_height = fm.height () * 3 / 2; + m_first_column_width = fm.width (QString::fromUtf8 ("LAYERNAME")); + m_column_width = m_pixmap_size + 4 + m_spacing + fm.width (QString::fromUtf8 ("1.00G ")); + + m_ellipsis = false; + } + + QSize sizeHint () const + { + return QSize (0, (m_line_height + m_spacing) * m_max_lines + m_font_height * 2 + m_spacing); + } + + void set_results (double dbu, int nx, int ny, const std::map, std::map, size_t> > &results, const std::map &count_per_layer, const std::vector &tolerances) + { + m_labels.clear (); + m_layer_labels.clear (); + m_ellipsis = false; + m_red_images.clear (); + m_green_images.clear (); + m_blue_images.clear (); + m_yellow_images.clear (); + m_labels.clear (); + + m_tolerance_labels.clear (); + for (std::vector::const_iterator t = tolerances.begin (); t != tolerances.end (); ++t) { + m_tolerance_labels << tl::to_qstring (tl::sprintf ("%.12g", *t * dbu)); + } + + std::vector > counters; + counters.insert (counters.end (), count_per_layer.begin (), count_per_layer.end ()); + std::sort (counters.begin (), counters.end (), CounterCompare ()); + + for (std::vector >::const_iterator c = counters.begin (); c != counters.end (); ++c) { + + if (m_layer_labels.size () == m_max_lines) { + m_ellipsis = true; + break; + } + + m_layer_labels << tl::to_qstring (c->first.to_string ()); + + m_labels.push_back (QStringList ()); + m_red_images.push_back (std::vector ()); + m_green_images.push_back (std::vector ()); + m_blue_images.push_back (std::vector ()); + m_yellow_images.push_back (std::vector ()); + + for (std::vector::const_iterator t = tolerances.begin (); t != tolerances.end (); ++t) { + + m_labels.back ().push_back (QString ()); + m_red_images.back ().push_back (QImage (m_pixmap_size, m_pixmap_size, QImage::Format_MonoLSB)); + m_green_images.back ().push_back (QImage (m_pixmap_size, m_pixmap_size, QImage::Format_MonoLSB)); + m_blue_images.back ().push_back (QImage (m_pixmap_size, m_pixmap_size, QImage::Format_MonoLSB)); + m_yellow_images.back ().push_back (QImage (m_pixmap_size, m_pixmap_size, QImage::Format_MonoLSB)); + + m_red_images.back ().back ().fill (Qt::black); + m_green_images.back ().back ().fill (Qt::black); + m_blue_images.back ().back ().fill (Qt::black); + m_yellow_images.back ().back ().fill (Qt::black); + + size_t tot_count = 0; + + for (std::map, std::map, size_t> >::const_iterator r = results.begin (); r != results.end (); ++r) { + + const std::map, size_t> &rm = r->second; + + std::map, size_t>::const_iterator rl = rm.find (std::make_pair (c->first, *t)); + if (rl != rm.end ()) { + + tot_count += rl->second; + + QImage *img = 0; + if (rl->second == 0) { + img = &m_green_images.back ().back (); + } else if (rl->second == missing_in_a) { + m_blue_images.back ().back ().fill (Qt::white); + } else if (rl->second == missing_in_b) { + m_yellow_images.back ().back ().fill (Qt::white); + } else { + img = &m_red_images.back ().back (); + } + + if (img) { + if (nx == 0 || ny == 0) { + img->fill (Qt::white); + } else { + + int ix = r->first.first; + int iy = r->first.second; + + int y2 = m_pixmap_size - 1 - (iy * m_pixmap_size + m_pixmap_size / 2) / ny; + int y1 = m_pixmap_size - 1 - ((iy + 1) * m_pixmap_size + m_pixmap_size / 2) / ny; + int x1 = (ix * m_pixmap_size + m_pixmap_size / 2) / nx; + int x2 = ((ix + 1) * m_pixmap_size + m_pixmap_size / 2) / nx; + + // "draw" the field + for (int y = y1; y <= y2 && y >= 0 && y < m_pixmap_size; ++y) { + *((uint32_t *) img->scanLine (y)) &= (((1 << x1) - 1) | ~((1 << (x2 + 1)) - 1)); + } + + } + } + + } + + } + + QString text; + if (tot_count == missing_in_a) { + text = QString::fromUtf8 ("B"); + } else if (tot_count == missing_in_b) { + text = QString::fromUtf8 ("A"); + } else if (tot_count > 1000000000) { + text = QString::fromUtf8 ("%1G").arg (tot_count * 1e-9, 0, 'f', 2); + } else if (tot_count > 100000000) { + text = QString::fromUtf8 ("%1M").arg (tot_count * 1e-6, 0, 'f', 0); + } else if (tot_count > 10000000) { + text = QString::fromUtf8 ("%1M").arg (tot_count * 1e-6, 0, 'f', 1); + } else if (tot_count > 1000000) { + text = QString::fromUtf8 ("%1M").arg (tot_count * 1e-6, 0, 'f', 2); + } else if (tot_count > 100000) { + text = QString::fromUtf8 ("%1k").arg (tot_count * 1e-3, 0, 'f', 0); + } else if (tot_count > 10000) { + text = QString::fromUtf8 ("%1k").arg (tot_count * 1e-3, 0, 'f', 1); + } else if (tot_count > 1000) { + text = QString::fromUtf8 ("%1k").arg (tot_count * 1e-3, 0, 'f', 2); + } else { + text = QString::fromUtf8 ("%1").arg (tot_count); + } + m_labels.back ().back () = text; + + } + + } + + update (); + } + + void paintEvent (QPaintEvent * /*ev*/) + { + QPainter painter (this); + + painter.drawText (QRect (QPoint (0, 0), QSize (m_first_column_width, m_font_height)), + tr ("Lay/Tol."), + QTextOption (Qt::AlignLeft | Qt::AlignTop)); + + for (int t = 0; t < m_tolerance_labels.size (); ++t) { + painter.drawText (QRect (QPoint (m_first_column_width + m_spacing + t * (m_column_width + m_spacing), 0), QSize (m_column_width, m_font_height)), + m_tolerance_labels [t], + QTextOption (Qt::AlignLeft | Qt::AlignTop)); + } + + for (int l = 0; l < m_layer_labels.size (); ++l) { + + painter.drawText (QRect (QPoint (0, m_font_height + m_spacing + l * (m_line_height + m_spacing)), QSize (m_first_column_width, m_line_height)), + m_layer_labels [l], + QTextOption (Qt::AlignLeft | Qt::AlignVCenter)); + + for (int t = 0; t < m_tolerance_labels.size (); ++t) { + + int x = m_first_column_width + m_spacing + t * (m_column_width + m_spacing); + int y = m_font_height + m_spacing + l * (m_line_height + m_spacing); + + painter.drawText (QRect (QPoint (x + m_pixmap_size + 4 + m_spacing, y), QSize (m_column_width, m_line_height)), + m_labels [l][t], + QTextOption (Qt::AlignLeft | Qt::AlignVCenter)); + + painter.save (); + + QLinearGradient grad (QPointF (0, 0), QPointF (1.0, 1.0)); + grad.setCoordinateMode (QGradient::ObjectBoundingMode); + grad.setColorAt (0.0, QColor (248, 248, 248)); + grad.setColorAt (1.0, QColor (224, 224, 224)); + painter.setBrush (QBrush (grad)); + painter.setPen (QPen (Qt::black)); + painter.drawRect (QRect (QPoint (x - 2, y - 2), QSize (m_pixmap_size + 3, m_pixmap_size + 3))); + + painter.setBackgroundMode (Qt::TransparentMode); + painter.setPen (QColor (128, 255, 128)); + painter.drawPixmap (x, y, QBitmap::fromImage (m_green_images [l][t])); + painter.setPen (QColor (255, 128, 128)); + painter.drawPixmap (x, y, QBitmap::fromImage (m_red_images [l][t])); + painter.setPen (QColor (128, 128, 255)); + painter.drawPixmap (x, y, QBitmap::fromImage (m_blue_images [l][t])); + painter.setPen (QColor (255, 255, 128)); + painter.drawPixmap (x, y, QBitmap::fromImage (m_yellow_images [l][t])); + painter.restore (); + + } + + } + + if (m_ellipsis) { + painter.drawText (QRect (QPoint (0, m_font_height + m_spacing + m_max_lines * (m_line_height + m_spacing)), QSize (m_first_column_width, m_font_height)), + QString::fromUtf8 ("..."), + QTextOption (Qt::AlignLeft | Qt::AlignTop)); + } + + } + +private: + int m_pixmap_size; + int m_line_height; + int m_font_height; + int m_max_lines; + int m_spacing; + int m_column_width; + int m_first_column_width; + QStringList m_tolerance_labels; + QStringList m_layer_labels; + std::vector m_labels; + std::vector > m_green_images; + std::vector > m_red_images; + std::vector > m_yellow_images; + std::vector > m_blue_images; + bool m_ellipsis; +}; + +class XORProgress + : public tl::RelativeProgress +{ +public: + XORProgress (const std::string &title, size_t max_count, size_t yield_interval) + : tl::RelativeProgress (title, max_count, yield_interval), m_needs_update (true), m_dbu (1.0), m_nx (0), m_ny (0) + { + // .. nothing yet .. + } + + virtual QWidget *progress_widget () const + { + return new XORProgressWidget (); + } + + virtual void render_progress (QWidget *widget) const + { + XORProgressWidget *pw = dynamic_cast (widget); + if (pw) { + pw->set_results (m_dbu, m_nx, m_ny, m_results, m_count_per_layer, m_tolerances); + } + } + + void set_results (double dbu, int nx, int ny, const std::map, std::map, size_t> > &results, const std::map &count_per_layer, const std::vector &tol) + { + if (m_count_per_layer != count_per_layer) { + m_dbu = dbu; + m_nx = nx; + m_ny = ny; + m_results = results; + m_tolerances = tol; + m_count_per_layer = count_per_layer; + m_needs_update = true; + } + } + +private: + std::map, std::map, size_t> > m_results; + std::map m_count_per_layer; + std::vector m_tolerances; + bool m_needs_update; + double m_dbu; + int m_nx, m_ny; +}; + XORToolDialog::XORToolDialog (QWidget *parent) : QDialog (parent), mp_view (0) { @@ -348,6 +641,7 @@ XORToolDialog::output_changed (int index) mp_ui->layer_offset_le->setEnabled (enabled); } + class XORJob : public tl::JobBase { @@ -387,7 +681,9 @@ public: m_sub_output_layers (sub_output_layers), m_rdb (rdb), m_rdb_cell (rdb_cell), - m_progress (0) + m_progress (0), + m_update_results (false), + m_nx (0), m_ny (0) { // .. nothing yet .. } @@ -412,9 +708,11 @@ public: return m_has_tiles; } - void has_tiles (bool ht) + void has_tiles (bool ht, int nx, int ny) { m_has_tiles = ht; + m_nx = ht ? nx : 0; + m_ny = ht ? ny : 0; } double dbu () const @@ -448,13 +746,32 @@ public: ++m_progress; } - void update_progress (tl::RelativeProgress &progress) + void add_results (const db::LayerProperties &lp, db::Coord tol, size_t n, size_t ix, size_t iy) + { + QMutexLocker locker (&m_mutex); + if (n == missing_in_a || n == missing_in_b) { + m_results [std::make_pair (ix, iy)][std::make_pair (lp, tol)] = n; + m_count_per_layer [lp] = n; + } else { + // NOTE: we will not get a "normal" n after missing_in_a or missing_in_b + m_results [std::make_pair (ix, iy)][std::make_pair (lp, tol)] += n; + m_count_per_layer [lp] += n; + } + + m_update_results = true; + } + + void update_progress (XORProgress &progress) { unsigned int p; { QMutexLocker locker (&m_mutex); p = m_progress; - } + if (m_update_results) { + progress.set_results (m_dbu, m_nx, m_ny, m_results, m_count_per_layer, m_tolerances); + m_update_results = false; + } + } progress.set (p, true /*force yield*/); } @@ -524,14 +841,19 @@ private: rdb::Cell *m_rdb_cell; unsigned int m_progress; QMutex m_mutex; + std::string m_result_string; + bool m_update_results; + size_t m_nx, m_ny; + std::map, std::map, size_t> > m_results; + std::map m_count_per_layer; }; class XORTask : public tl::Task { public: - XORTask (const std::string &tile_desc, const db::Box &clip_box, const db::Box ®ion_a, const db::Box ®ion_b, unsigned int layer_index, const db::LayerProperties &lp, const std::vector &la, const std::vector &lb) - : m_tile_desc (tile_desc), m_clip_box (clip_box), m_region_a (region_a), m_region_b (region_b), m_layer_index (layer_index), m_lp (lp), m_la (la), m_lb (lb) + XORTask (const std::string &tile_desc, const db::Box &clip_box, const db::Box ®ion_a, const db::Box ®ion_b, unsigned int layer_index, const db::LayerProperties &lp, const std::vector &la, const std::vector &lb, int ix, int iy) + : m_tile_desc (tile_desc), m_clip_box (clip_box), m_region_a (region_a), m_region_b (region_b), m_layer_index (layer_index), m_lp (lp), m_la (la), m_lb (lb), m_ix (ix), m_iy (iy) { // .. nothing yet .. } @@ -576,12 +898,23 @@ public: return m_lp; } + int ix () const + { + return m_ix; + } + + int iy () const + { + return m_iy; + } + private: std::string m_tile_desc; db::Box m_clip_box, m_region_a, m_region_b; unsigned int m_layer_index; db::LayerProperties m_lp; std::vector m_la, m_lb; + int m_ix, m_iy; }; class XORWorker @@ -789,6 +1122,8 @@ XORWorker::do_perform (const XORTask *xor_task) db::CplxTrans trans = db::CplxTrans (mp_job->dbu ()); + size_t n = 0; + for (db::Shapes::shape_iterator s = xor_results_cell.shapes (0).begin (db::ShapeIterator::All); ! s.at_end (); ++s) { if (mp_job->has_tiles ()) { @@ -798,24 +1133,30 @@ XORWorker::do_perform (const XORTask *xor_task) for (std::vector ::const_iterator cp = clipped_poly.begin (); cp != clipped_poly.end (); ++cp) { mp_job->issue_polygon (tol_index, xor_task->layer_index (), *cp, trans); + ++n; } } else { mp_job->issue_polygon (tol_index, xor_task->layer_index (), s->polygon (), trans); + ++n; } } + mp_job->add_results (xor_task->lp (), *t, n, xor_task->ix (), xor_task->iy ()); + } else if (mp_job->op () == db::BooleanOp::Xor || (mp_job->op () == db::BooleanOp::ANotB && !la.empty ()) || (mp_job->op () == db::BooleanOp::BNotA && !lb.empty ())) { if (!la.empty ()) { mp_job->issue_string (tol_index, xor_task->layer_index (), tl::to_string (QObject::tr ("Layer not present at all in layout B"))); + mp_job->add_results (xor_task->lp (), *t, missing_in_b, 0, 0); } if (!lb.empty ()) { mp_job->issue_string (tol_index, xor_task->layer_index (), tl::to_string (QObject::tr ("Layer not present at all in layout A"))); + mp_job->add_results (xor_task->lp (), *t, missing_in_a, 0, 0); } } @@ -1249,7 +1590,7 @@ XORToolDialog::run_xor () db::Coord tile_enlargement_b = db::coord_traits::rounded_up (tile_enlargement * dbu / cvb->layout ().dbu ()); if (ntiles_w > 1 || ntiles_h > 1 || region_mode != RMAll /*enforces clip*/) { - job.has_tiles (true); + job.has_tiles (true, ntiles_w, ntiles_h); } // create the XOR tasks @@ -1279,7 +1620,7 @@ XORToolDialog::run_xor () unsigned int layer_index = 0; for (std::map, std::vector >, db::LPLogicalLessFunc>::const_iterator l = layers.begin (); l != layers.end (); ++l, ++layer_index) { - job.schedule (new XORTask (tile_desc, clip_box, region_a, region_b, layer_index, l->first, l->second.first, l->second.second)); + job.schedule (new XORTask (tile_desc, clip_box, region_a, region_b, layer_index, l->first, l->second.first, l->second.second, nw, nh)); } } @@ -1298,7 +1639,7 @@ XORToolDialog::run_xor () // TODO: there should be a general scheme of how thread-specific progress is merged // into a global one .. - tl::RelativeProgress progress (tl::to_string (QObject::tr ("Performing ")) + op_name, todo_count, 1); + XORProgress progress (tl::to_string (QObject::tr ("Performing ")) + op_name, todo_count, 1); // We need to lock the layouts during the processing - in OMNewLayerA and OMNewLayerB mode // we actually modify the layout we iterate over diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 68acc4816..d5d58df0b 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -150,6 +150,21 @@ public: mp_progress_widget->set_value (v, value); } + void add_widget (QWidget *widget) + { + mp_progress_widget->add_widget (widget); + } + + void remove_widget () + { + mp_progress_widget->remove_widget (); + } + + QWidget *get_widget () const + { + return mp_progress_widget->get_widget (); + } + private: lay::ProgressWidget *mp_progress_widget; lay::ProgressReporter *mp_pr; @@ -391,6 +406,30 @@ void TextProgressDelegate::show_progress_bar (bool show) } } +bool TextProgressDelegate::progress_wants_widget () const +{ + return mp_mw != 0 && mp_mw->progress_wants_widget (); +} + +void TextProgressDelegate::progress_add_widget (QWidget *widget) +{ + if (mp_mw) { + mp_mw->progress_add_widget (widget); + } +} + +QWidget *TextProgressDelegate::progress_get_widget () const +{ + return mp_mw ? mp_mw->progress_get_widget () : 0; +} + +void TextProgressDelegate::progress_remove_widget () +{ + if (mp_mw) { + mp_mw->progress_remove_widget (); + } +} + // ------------------------------------------------------------- static MainWindow *mw_instance = 0; @@ -4593,6 +4632,18 @@ MainWindow::clear_current_pos () mp_cpy_label->setText (QString ()); } +QWidget * +MainWindow::progress_get_widget () const +{ + if (mp_progress_dialog) { + return mp_progress_dialog->get_widget (); + } else if ( mp_progress_widget) { + return mp_progress_widget->get_widget (); + } else { + return 0; + } +} + bool MainWindow::set_progress_can_cancel (bool f) { @@ -4635,6 +4686,32 @@ MainWindow::set_progress_value (double v, const std::string &value) } } +bool +MainWindow::progress_wants_widget () const +{ + return true; +} + +void +MainWindow::progress_add_widget (QWidget *widget) +{ + if (mp_progress_dialog) { + mp_progress_dialog->add_widget (widget); + } else if (mp_progress_widget) { + mp_progress_widget->add_widget (widget); + } +} + +void +MainWindow::progress_remove_widget () +{ + if (mp_progress_dialog) { + mp_progress_dialog->remove_widget (); + } else if (mp_progress_widget) { + mp_progress_widget->remove_widget (); + } +} + bool MainWindow::show_progress_bar (bool show) { diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index b6d13c12a..666abd0c4 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -108,6 +108,10 @@ public: virtual void set_progress_text (const std::string &text); virtual void set_progress_value (double v, const std::string &value); virtual void show_progress_bar (bool show); + virtual bool progress_wants_widget () const; + virtual void progress_add_widget (QWidget *widget); + virtual QWidget *progress_get_widget () const; + virtual void progress_remove_widget (); private: MainWindow *mp_mw; @@ -388,6 +392,26 @@ public: */ bool set_progress_value (double v, const std::string &value); + /** + * @brief Implementation of the lay::ProgressBar interface: Returns a value indicating whether a progress widget is wanted + */ + bool progress_wants_widget () const; + + /** + * @brief Implementation of the lay::ProgressBar interface: adds a progress widget + */ + void progress_add_widget (QWidget *widget); + + /** + * @brief Implementation of the lay::ProgressBar interface: gets the progress widget + */ + QWidget *progress_get_widget () const; + + /** + * @brief Implementation of the lay::ProgressBar interface: removes a progress widget + */ + void progress_remove_widget (); + /** * @brief Implementation of the lay::ProgressBar interface: Make the progress widget visible or invisible */ diff --git a/src/lay/lay/layProgress.cc b/src/lay/lay/layProgress.cc index d6b8b6694..44dd0b581 100644 --- a/src/lay/lay/layProgress.cc +++ b/src/lay/lay/layProgress.cc @@ -168,6 +168,10 @@ ProgressReporter::update_and_yield () mp_pb->set_progress_can_cancel (mp_objects.front ()->can_cancel ()); mp_pb->set_progress_text (mp_objects.front ()->desc ()); mp_pb->set_progress_value (mp_objects.front ()->value (), mp_objects.front ()->formatted_value ()); + QWidget *w = mp_pb->progress_get_widget (); + if (w) { + mp_objects.front ()->render_progress (w); + } } process_events (); // Qt4 seems to need this } @@ -189,12 +193,20 @@ ProgressReporter::set_visible (bool vis) } if (vis != m_pw_visible) { + // prevent deferred method execution inside progress events - this might interfere with the // actual operation tl::DeferredMethodScheduler::enable (!vis); - } - m_pw_visible = vis; + if (!vis) { + mp_pb->progress_remove_widget (); + } else if (mp_pb->progress_wants_widget () && mp_objects.front ()) { + mp_pb->progress_add_widget (mp_objects.front ()->progress_widget ()); + } + + m_pw_visible = vis; + + } if (QApplication::instance()) { if (vis) { diff --git a/src/lay/lay/layProgress.h b/src/lay/lay/layProgress.h index db644dc33..482a1c838 100644 --- a/src/lay/lay/layProgress.h +++ b/src/lay/lay/layProgress.h @@ -50,6 +50,10 @@ public: virtual void set_progress_can_cancel (bool f) = 0; virtual void set_progress_text (const std::string &text) = 0; virtual void set_progress_value (double v, const std::string &value) = 0; + virtual bool progress_wants_widget () const { return false; } + virtual void progress_add_widget (QWidget * /*widget*/) { } + virtual void progress_remove_widget () { } + virtual QWidget *progress_get_widget () const { return 0; } virtual void show_progress_bar (bool show) = 0; }; diff --git a/src/lay/lay/layProgressWidget.cc b/src/lay/lay/layProgressWidget.cc index 828b065b9..941b3e809 100644 --- a/src/lay/lay/layProgressWidget.cc +++ b/src/lay/lay/layProgressWidget.cc @@ -26,6 +26,7 @@ #include #include +#include namespace lay { @@ -127,7 +128,7 @@ ProgressBarWidget::resizeEvent (QResizeEvent *) ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full_width) : QFrame (parent), - mp_pr (pr) + mp_widget (0), mp_pr (pr) { QVBoxLayout *top_layout = new QVBoxLayout (this); top_layout->addStretch (1); @@ -140,24 +141,30 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full // this does not allow the label to control the overall size, so a long string does not hurt: bar_frame->setSizePolicy (QSizePolicy::Ignored, QSizePolicy::Preferred); - QHBoxLayout *layout = new QHBoxLayout (bar_frame); + QGridLayout *layout = new QGridLayout (bar_frame); + mp_layout = layout; layout->setSpacing (4); layout->setMargin (0); + int col = 0; + if (! full_width) { - layout->addStretch (1); + layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1); + layout->setColumnStretch (col++, 1); } mp_label = new QLabel (bar_frame); - layout->addWidget (mp_label); + layout->addWidget (mp_label, 0, col++, 1, 1); - layout->addSpacing (8); + layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Fixed, QSizePolicy::Fixed), 0, col++, 1, 1); QFrame *progress_bar_frame = new QFrame (bar_frame); progress_bar_frame->setFrameStyle (QFrame::Box | QFrame::Plain); progress_bar_frame->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Preferred); - layout->addWidget (progress_bar_frame, 2); + layout->addWidget (progress_bar_frame, 0, col, 1, 1); + m_widget_col = col; + layout->setColumnStretch(col++, 2); QHBoxLayout *pbf_layout = new QHBoxLayout (progress_bar_frame); progress_bar_frame->setLayout (pbf_layout); @@ -166,19 +173,47 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full mp_progress_bar = new ProgressBarWidget (progress_bar_frame); pbf_layout->addWidget (mp_progress_bar); - layout->addSpacing (8); + layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Fixed, QSizePolicy::Fixed), 0, col++, 1, 1); mp_cancel_button = new QToolButton (bar_frame); mp_cancel_button->setText (QObject::tr ("Cancel")); - layout->addWidget (mp_cancel_button); + layout->addWidget (mp_cancel_button, 0, col++, 1, 1); if (! full_width) { - layout->addStretch (1); + layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1); + layout->setColumnStretch (col++, 1); } connect (mp_cancel_button, SIGNAL (clicked ()), this, SLOT (signal_break ())); } +QWidget * +ProgressWidget::get_widget () const +{ + return mp_widget; +} + +void +ProgressWidget::add_widget (QWidget *widget) +{ + remove_widget (); + + if (widget) { + mp_widget = widget; + widget->setParent(this); + mp_layout->addWidget (widget, 1, m_widget_col, 1, 1); + } +} + +void +ProgressWidget::remove_widget () +{ + if (mp_widget) { + delete mp_widget; + mp_widget = 0; + } +} + void ProgressWidget::set_can_cancel (bool f) { diff --git a/src/lay/lay/layProgressWidget.h b/src/lay/lay/layProgressWidget.h index d85008bd5..c6bec2ad5 100644 --- a/src/lay/lay/layProgressWidget.h +++ b/src/lay/lay/layProgressWidget.h @@ -35,6 +35,7 @@ class QToolButton; class QLabel; class QToolButton; +class QGridLayout; namespace lay { @@ -52,6 +53,9 @@ public: void set_text (const std::string &text); void set_value (double v, const std::string &value); void set_can_cancel (bool f); + void add_widget (QWidget *widget); + void remove_widget (); + QWidget *get_widget () const; QSize sizeHint () const; @@ -61,6 +65,9 @@ public slots: private: QLabel *mp_label; ProgressBarWidget *mp_progress_bar; + QWidget *mp_widget; + int m_widget_col; + QGridLayout *mp_layout; QToolButton *mp_cancel_button; ProgressReporter *mp_pr; }; diff --git a/src/tl/tl/tlProgress.h b/src/tl/tl/tlProgress.h index 04f1470e1..809ae87bb 100644 --- a/src/tl/tl/tlProgress.h +++ b/src/tl/tl/tlProgress.h @@ -30,6 +30,8 @@ #include "tlException.h" #include "tlTimer.h" +class QWidget; + namespace tl { @@ -120,6 +122,19 @@ public: */ virtual double value () const = 0; + /** + * @brief Creates a widget that renders the progress graphically + * + * The widget is not the progress bar - the progress bar is always shown. + * This method returns 0 if no graphical representation is required. + */ + virtual QWidget *progress_widget () const { return 0; } + + /** + * @brief Renders the progress on the widget that was created by progress_widget + */ + virtual void render_progress (QWidget * /*widget*/) const { } + /** * @brief Set a value indicating whether the operation can be cancelled *