diff --git a/tools/lammps-gui/go-first.png b/tools/lammps-gui/go-first.png new file mode 100644 index 0000000000..d018049876 Binary files /dev/null and b/tools/lammps-gui/go-first.png differ diff --git a/tools/lammps-gui/go-last.png b/tools/lammps-gui/go-last.png new file mode 100644 index 0000000000..fe382ca6cb Binary files /dev/null and b/tools/lammps-gui/go-last.png differ diff --git a/tools/lammps-gui/go-next-2.png b/tools/lammps-gui/go-next-2.png new file mode 100644 index 0000000000..1d99703b7a Binary files /dev/null and b/tools/lammps-gui/go-next-2.png differ diff --git a/tools/lammps-gui/go-previous-2.png b/tools/lammps-gui/go-previous-2.png new file mode 100644 index 0000000000..bfd558bc20 Binary files /dev/null and b/tools/lammps-gui/go-previous-2.png differ diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index b340ab766d..5f6b54d373 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -552,6 +552,10 @@ void LammpsGui::open_file(const QString &fileName) dirstatus->setText(QString(" Directory: ") + current_dir); status->setText("Ready."); + if (slideshow) { + delete slideshow; + slideshow = nullptr; + } update_variables(); } @@ -717,20 +721,25 @@ void LammpsGui::logupdate() } } + // get timestep + int step = 0; + void *ptr = lammps.last_thermo("step", 0); + if (ptr) { + if (lammps.extract_setting("bigint") == 4) + step = *(int *)ptr; + else + step = (int)*(int64_t *)ptr; + } + // extract cached thermo data if (chartwindow) { // thermo data is not yet valid during setup void *ptr = lammps.last_thermo("setup", 0); if (ptr && *(int *)ptr) return; - ptr = lammps.last_thermo("step", 0); + ptr = lammps.last_thermo("num", 0); if (ptr) { - int step = 0; - if (lammps.extract_setting("bigint") == 4) - step = *(int *)ptr; - else - step = (int)*(int64_t *)ptr; - int ncols = *(int *)lammps.last_thermo("num", 0); + int ncols = *(int *)ptr; // check if the column assignment has changed // if yes, delete charts and start over @@ -775,6 +784,22 @@ void LammpsGui::logupdate() } } } + + // update list of available image file names + + QString imagefile = (const char *)lammps.last_thermo("imagename", 0); + if (!imagefile.isEmpty()) { + if (!slideshow) { + slideshow = new SlideShow(current_file); + if (QSettings().value("viewslide", true).toBool()) + slideshow->show(); + else + slideshow->hide(); + } else { + slideshow->setWindowTitle(QString("LAMMPS-GUI - Slide Show: ") + current_file); + } + slideshow->add_image(imagefile); + } } void LammpsGui::modified() @@ -963,6 +988,12 @@ void LammpsGui::do_run(bool use_buffer) else chartwindow->hide(); + if (slideshow) { + slideshow->setWindowTitle("LAMMPS-GUI - Slide Show"); + slideshow->clear(); + slideshow->hide(); + } + logupdater = new QTimer(this); connect(logupdater, &QTimer::timeout, this, &LammpsGui::logupdate); logupdater->start(100); @@ -991,7 +1022,7 @@ void LammpsGui::render_image() void LammpsGui::view_slides() { - if (!slideshow) slideshow = new SlideShow(current_file, &lammps); + if (!slideshow) slideshow = new SlideShow(current_file); if (slideshow->isVisible()) slideshow->hide(); else diff --git a/tools/lammps-gui/lammpsgui.qrc b/tools/lammps-gui/lammpsgui.qrc index 15ff13da4b..a27d6abbea 100644 --- a/tools/lammps-gui/lammpsgui.qrc +++ b/tools/lammps-gui/lammpsgui.qrc @@ -1,50 +1,57 @@ + - - lammps-icon-128x128.png - antialias.png - application-calc.png - application-exit.png - application-plot.png - axes-img.png - document-new.png - document-open-recent.png - document-open.png - document-revert.png - document-save-as.png - document-save.png - edit-copy.png - edit-cut.png - edit-delete.png - edit-paste.png - edit-redo.png - edit-undo.png - emblem-photos.png - gtk-go-down.png - gtk-go-up.png - gtk-zoom-fit.png - gtk-zoom-in.png - gtk-zoom-out.png - hd-img.png - help-about.png - help-browser.png - help-faq.png - help_index.table - image-x-generic.png - object-rotate-left.png - object-rotate-right.png - ovito.png - preferences-desktop-font.png - preferences-desktop-personal.png - preferences-desktop.png - process-stop.png - run-file.png - system-box.png - system-help.png - system-run.png - utilities-terminal.png - vdw-style.png - vmd.png - window-close.png - x-office-drawing.png - + + lammps-icon-128x128.png + antialias.png + application-calc.png + application-exit.png + application-plot.png + axes-img.png + document-new.png + document-open-recent.png + document-open.png + document-revert.png + document-save-as.png + document-save.png + edit-copy.png + edit-cut.png + edit-delete.png + edit-paste.png + edit-redo.png + edit-undo.png + emblem-photos.png + go-first.png + go-last.png + go-next-2.png + go-previous-2.png + gtk-go-down.png + gtk-go-up.png + gtk-zoom-fit.png + gtk-zoom-in.png + gtk-zoom-out.png + hd-img.png + help-about.png + help-browser.png + help-faq.png + help_index.table + image-x-generic.png + media-playback-start-2.png + media-playlist-repeat.png + object-rotate-left.png + object-rotate-right.png + ovito.png + preferences-desktop-font.png + preferences-desktop-personal.png + preferences-desktop.png + process-stop.png + run-file.png + system-box.png + system-help.png + system-run.png + utilities-terminal.png + vdw-style.png + vmd.png + window-close.png + x-office-drawing.png + diff --git a/tools/lammps-gui/lammpsgui.ui b/tools/lammps-gui/lammpsgui.ui index f5d9f92949..77257b23c2 100644 --- a/tools/lammps-gui/lammpsgui.ui +++ b/tools/lammps-gui/lammpsgui.ui @@ -361,7 +361,7 @@ &Slide Show Window - Ctrl+Shift+W + Ctrl+L diff --git a/tools/lammps-gui/media-playback-start-2.png b/tools/lammps-gui/media-playback-start-2.png new file mode 100644 index 0000000000..82bfaea9a4 Binary files /dev/null and b/tools/lammps-gui/media-playback-start-2.png differ diff --git a/tools/lammps-gui/media-playlist-repeat.png b/tools/lammps-gui/media-playlist-repeat.png new file mode 100644 index 0000000000..8845699912 Binary files /dev/null and b/tools/lammps-gui/media-playlist-repeat.png differ diff --git a/tools/lammps-gui/preferences.cpp b/tools/lammps-gui/preferences.cpp index bdb7d39dbf..eb8d0fc8e2 100644 --- a/tools/lammps-gui/preferences.cpp +++ b/tools/lammps-gui/preferences.cpp @@ -161,6 +161,8 @@ void Preferences::accept() if (box) settings->setValue("viewlog", box->isChecked()); box = tabWidget->findChild("viewchart"); if (box) settings->setValue("viewchart", box->isChecked()); + box = tabWidget->findChild("viewslide"); + if (box) settings->setValue("viewslide", box->isChecked()); if (need_relaunch) { QMessageBox msg(QMessageBox::Information, QString("Relaunching LAMMPS-GUI"), @@ -192,6 +194,9 @@ GeneralTab::GeneralTab(QSettings *_settings, LammpsWrapper *_lammps, QWidget *pa auto *pltv = new QCheckBox("Show chart window by default"); pltv->setObjectName("viewchart"); pltv->setCheckState(settings->value("viewchart", true).toBool() ? Qt::Checked : Qt::Unchecked); + auto *sldv = new QCheckBox("Show slide show by default"); + sldv->setObjectName("viewslide"); + sldv->setCheckState(settings->value("viewslide", true).toBool() ? Qt::Checked : Qt::Unchecked); auto *logr = new QCheckBox("Replace log window on new run"); logr->setObjectName("logreplace"); logr->setCheckState(settings->value("logreplace", false).toBool() ? Qt::Checked @@ -232,6 +237,7 @@ GeneralTab::GeneralTab(QSettings *_settings, LammpsWrapper *_lammps, QWidget *pa layout->addWidget(cite); layout->addWidget(logv); layout->addWidget(pltv); + layout->addWidget(sldv); layout->addWidget(logr); layout->addWidget(pltr); layout->addWidget(imgr); diff --git a/tools/lammps-gui/slideshow.cpp b/tools/lammps-gui/slideshow.cpp index cc8c57d4f0..d949907668 100644 --- a/tools/lammps-gui/slideshow.cpp +++ b/tools/lammps-gui/slideshow.cpp @@ -12,177 +12,240 @@ ------------------------------------------------------------------------- */ #include "slideshow.h" -#include "lammpswrapper.h" -#include #include -#include -#include -#include +#include +#include #include #include #include -#include -#include #include -#include #include -#include -#include -#include #include -#include +#include +#include +#include #include -#include -#include - -static const QString blank(" "); - -SlideShow::SlideShow(const QString &fileName, LammpsWrapper *_lammps, QWidget *parent) : - QDialog(parent), imageLabel(new QLabel), scrollArea(new QScrollArea), menuBar(new QMenuBar), - lammps(_lammps) +SlideShow::SlideShow(const QString &fileName, QWidget *parent) : + QDialog(parent), playtimer(nullptr), imageLabel(new QLabel), imageName(new QLabel("(none)")), + do_loop(true) { imageLabel->setBackgroundRole(QPalette::Base); - imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); - imageLabel->setScaledContents(true); - imageLabel->minimumSizeHint(); + imageLabel->setScaledContents(false); + imageLabel->setMinimumSize(100, 100); - scrollArea->setBackgroundRole(QPalette::Dark); - scrollArea->setWidget(imageLabel); - scrollArea->setVisible(false); + imageName->setFrameStyle(QFrame::Raised); + imageName->setFrameShape(QFrame::Panel); + imageName->setAlignment(Qt::AlignCenter); + + auto *shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), this); + QObject::connect(shortcut, &QShortcut::activated, this, &SlideShow::close); buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - QVBoxLayout *mainLayout = new QVBoxLayout; - QSettings settings; + auto *mainLayout = new QVBoxLayout; + auto *navLayout = new QHBoxLayout; - mainLayout->addWidget(scrollArea); + auto *gofirst = new QPushButton(QIcon(":/go-first.png"), ""); + gofirst->setToolTip("Go to first Image"); + auto *goprev = new QPushButton(QIcon(":/go-previous-2.png"), ""); + goprev->setToolTip("Go to previous Image"); + auto *goplay = new QPushButton(QIcon(":/media-playback-start-2.png"), ""); + goplay->setToolTip("Play animation"); + goplay->setCheckable(true); + goplay->setChecked(playtimer); + goplay->setObjectName("play"); + auto *gonext = new QPushButton(QIcon(":/go-next-2.png"), ""); + gonext->setToolTip("Go to next Image"); + auto *golast = new QPushButton(QIcon(":/go-last.png"), ""); + golast->setToolTip("Go to last Image"); + auto *goloop = new QPushButton(QIcon(":/media-playlist-repeat.png"), ""); + goloop->setToolTip("Loop animation"); + goloop->setCheckable(true); + goloop->setChecked(do_loop); + + auto *zoomin = new QPushButton(QIcon(":/gtk-zoom-in.png"), ""); + zoomin->setToolTip("Zoom in by 10 percent"); + auto *zoomout = new QPushButton(QIcon(":/gtk-zoom-out.png"), ""); + zoomout->setToolTip("Zoom out by 10 percent"); + auto *normal = new QPushButton(QIcon(":/gtk-zoom-fit.png"), ""); + normal->setToolTip("Reset zoom to normal"); + + connect(gofirst, &QPushButton::released, this, &SlideShow::first); + connect(goprev, &QPushButton::released, this, &SlideShow::prev); + connect(goplay, &QPushButton::released, this, &SlideShow::play); + connect(gonext, &QPushButton::released, this, &SlideShow::next); + connect(golast, &QPushButton::released, this, &SlideShow::last); + connect(goloop, &QPushButton::released, this, &SlideShow::loop); + connect(zoomin, &QPushButton::released, this, &SlideShow::zoomIn); + connect(zoomout, &QPushButton::released, this, &SlideShow::zoomOut); + connect(gofirst, &QPushButton::released, this, &SlideShow::first); + connect(normal, &QPushButton::released, this, &SlideShow::normalSize); + + navLayout->addWidget(imageName); + navLayout->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum)); + navLayout->addWidget(gofirst); + navLayout->addWidget(goprev); + navLayout->addWidget(goplay); + navLayout->addWidget(gonext); + navLayout->addWidget(golast); + navLayout->addWidget(goloop); + + navLayout->addWidget(zoomin); + navLayout->addWidget(zoomout); + navLayout->addWidget(normal); + + mainLayout->addWidget(imageLabel); + mainLayout->addLayout(navLayout); mainLayout->addWidget(buttonBox); - setWindowTitle(QString("Slide Show: ") + QFileInfo(fileName).fileName()); - createActions(); + setWindowTitle(QString("LAMMPS-GUI - Slide Show: ") + QFileInfo(fileName).fileName()); + imagefiles.clear(); scaleFactor = 1.0; - resize(image.width() + 20, image.height() + 50); + current = 0; - scrollArea->setVisible(true); - fitToWindowAct->setEnabled(true); - updateActions(); - if (!fitToWindowAct->isChecked()) imageLabel->adjustSize(); setLayout(mainLayout); } -void SlideShow::saveAs() +void SlideShow::add_image(const QString &filename) { - QString fileName = QFileDialog::getSaveFileName(this, "Save Image File As", QString(), - "Image Files (*.jpg *.png *.bmp *.ppm)"); - saveFile(fileName); + if (!imagefiles.contains(filename)) { + int lastidx = imagefiles.size(); + imagefiles.append(filename); + loadImage(lastidx); + } } -void SlideShow::copy() {} +void SlideShow::clear() +{ + imagefiles.clear(); + image.fill(Qt::black); + imageLabel->setPixmap(QPixmap::fromImage(image)); + imageLabel->adjustSize(); + imageName->setText("(none)"); + repaint(); +} + +void SlideShow::loadImage(int idx) +{ + if ((idx < 0) || (idx >= imagefiles.size())) return; + + do { + QImageReader reader(imagefiles[idx]); + reader.setAutoTransform(true); + const QImage newImage = reader.read(); + + // There was an error reading the image file. Try reading the previous image instead. + if (newImage.isNull()) { + --idx; + } else { + int newheight = (int)newImage.height() * scaleFactor; + int newwidth = (int)newImage.width() * scaleFactor; + image = newImage.scaled(newwidth, newheight, Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + imageLabel->setPixmap(QPixmap::fromImage(image)); + imageLabel->setMinimumSize(newwidth, newheight); + imageName->setText(imagefiles[idx]); + adjustSize(); + current = idx; + break; + } + } while (idx >= 0); +} + +void SlideShow::first() +{ + current = 0; + loadImage(current); +} + +void SlideShow::last() +{ + current = imagefiles.size() - 1; + loadImage(current); +} + +void SlideShow::play() +{ + // if we do not loop, start animation from beginning + if (!do_loop) current = 0; + + if (playtimer) { + playtimer->stop(); + delete playtimer; + playtimer = nullptr; + } else { + playtimer = new QTimer(this); + connect(playtimer, &QTimer::timeout, this, &SlideShow::next); + playtimer->start(100); + } + + // reset push button state. use findChild() if not triggered from button. + QPushButton *button = qobject_cast(sender()); + if (!button) button = findChild("play"); + if (button) button->setChecked(playtimer); +} + +void SlideShow::next() +{ + ++current; + if (current >= imagefiles.size()) { + if (do_loop) { + current = 0; + } else { + // stop animation + if (playtimer) play(); + --current; + } + } + loadImage(current); +} + +void SlideShow::prev() +{ + --current; + if (current < 0) { + if (do_loop) + current = imagefiles.size() - 1; + else + current = 0; + } + loadImage(current); +} + +void SlideShow::loop() +{ + QPushButton *button = qobject_cast(sender()); + do_loop = !do_loop; + button->setChecked(do_loop); +} void SlideShow::zoomIn() { - scaleImage(1.25); + scaleImage(1.1); } void SlideShow::zoomOut() { - scaleImage(0.8); + scaleImage(0.9); } void SlideShow::normalSize() { - imageLabel->adjustSize(); scaleFactor = 1.0; -} - -void SlideShow::fitToWindow() -{ - bool fitToWindow = fitToWindowAct->isChecked(); - scrollArea->setWidgetResizable(fitToWindow); - if (!fitToWindow) normalSize(); - updateActions(); -} - -void SlideShow::saveFile(const QString &fileName) -{ - if (!fileName.isEmpty()) image.save(fileName); -} - -void SlideShow::createActions() -{ - QMenu *fileMenu = menuBar->addMenu("&File"); - - saveAsAct = fileMenu->addAction("&Save As...", this, &SlideShow::saveAs); - saveAsAct->setIcon(QIcon(":/document-save-as.png")); - saveAsAct->setEnabled(false); - fileMenu->addSeparator(); - copyAct = fileMenu->addAction("&Copy", this, &SlideShow::copy); - copyAct->setIcon(QIcon(":/edit-copy.png")); - copyAct->setShortcut(QKeySequence::Copy); - copyAct->setEnabled(false); - fileMenu->addSeparator(); - QAction *exitAct = fileMenu->addAction("&Close", this, &QWidget::close); - exitAct->setIcon(QIcon(":/window-close.png")); - exitAct->setShortcut(QKeySequence::fromString("Ctrl+W")); - - QMenu *viewMenu = menuBar->addMenu("&View"); - - zoomInAct = viewMenu->addAction("Image Zoom &In (25%)", this, &SlideShow::zoomIn); - zoomInAct->setShortcut(QKeySequence::ZoomIn); - zoomInAct->setIcon(QIcon(":/gtk-zoom-in.png")); - zoomInAct->setEnabled(false); - - zoomOutAct = viewMenu->addAction("Image Zoom &Out (25%)", this, &SlideShow::zoomOut); - zoomOutAct->setShortcut(QKeySequence::ZoomOut); - zoomOutAct->setIcon(QIcon(":/gtk-zoom-out.png")); - zoomOutAct->setEnabled(false); - - normalSizeAct = viewMenu->addAction("&Reset Image Size", this, &SlideShow::normalSize); - normalSizeAct->setShortcut(QKeySequence::fromString("Ctrl+0")); - normalSizeAct->setIcon(QIcon(":/gtk-zoom-fit.png")); - normalSizeAct->setEnabled(false); - - viewMenu->addSeparator(); - - fitToWindowAct = viewMenu->addAction("&Fit to Window", this, &SlideShow::fitToWindow); - fitToWindowAct->setEnabled(false); - fitToWindowAct->setCheckable(true); - fitToWindowAct->setShortcut(QKeySequence::fromString("Ctrl+=")); -} - -void SlideShow::updateActions() -{ - saveAsAct->setEnabled(!image.isNull()); - copyAct->setEnabled(!image.isNull()); - zoomInAct->setEnabled(!fitToWindowAct->isChecked()); - zoomOutAct->setEnabled(!fitToWindowAct->isChecked()); - normalSizeAct->setEnabled(!fitToWindowAct->isChecked()); + scaleImage(1.0); } void SlideShow::scaleImage(double factor) { scaleFactor *= factor; -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - imageLabel->resize(scaleFactor * imageLabel->pixmap()->size()); -#else - imageLabel->resize(scaleFactor * imageLabel->pixmap(Qt::ReturnByValue).size()); -#endif - - adjustScrollBar(scrollArea->horizontalScrollBar(), factor); - adjustScrollBar(scrollArea->verticalScrollBar(), factor); - zoomInAct->setEnabled(scaleFactor < 3.0); - zoomOutAct->setEnabled(scaleFactor > 0.333); -} - -void SlideShow::adjustScrollBar(QScrollBar *scrollBar, double factor) -{ - scrollBar->setValue( - int(factor * scrollBar->value() + ((factor - 1) * scrollBar->pageStep() / 2))); + if (scaleFactor > 2.0) scaleFactor = 2.0; + if (scaleFactor < 0.25) scaleFactor = 0.25; + loadImage(current); } // Local Variables: diff --git a/tools/lammps-gui/slideshow.h b/tools/lammps-gui/slideshow.h index bde011e905..1bf62a99bb 100644 --- a/tools/lammps-gui/slideshow.h +++ b/tools/lammps-gui/slideshow.h @@ -14,60 +14,48 @@ #ifndef SLIDESHOW_H #define SLIDESHOW_H -#include #include #include #include +#include -class QAction; -class QMenuBar; class QDialogButtonBox; class QLabel; -class QObject; -class QScrollArea; -class QScrollBar; -class QStatusBar; -class LammpsWrapper; -class QComboBox; +class QTimer; class SlideShow : public QDialog { Q_OBJECT public: - explicit SlideShow(const QString &fileName, LammpsWrapper *_lammps, - QWidget *parent = nullptr); + explicit SlideShow(const QString &fileName, QWidget *parent = nullptr); + void add_image(const QString &filename); + void clear(); private slots: - void saveAs(); - void copy(); + void first(); + void last(); + void next(); + void prev(); + void play(); + void loop(); void zoomIn(); void zoomOut(); void normalSize(); - void fitToWindow(); private: - void createActions(); - void updateActions(); - void saveFile(const QString &fileName); void scaleImage(double factor); - void adjustScrollBar(QScrollBar *scrollBar, double factor); + void loadImage(int idx); private: QImage image; - QMenuBar *menuBar; - QLabel *imageLabel; - QScrollArea *scrollArea; + QTimer *playtimer; + QLabel *imageLabel, *imageName; QDialogButtonBox *buttonBox; double scaleFactor = 1.0; - QAction *saveAsAct; - QAction *copyAct; - QAction *zoomInAct; - QAction *zoomOutAct; - QAction *normalSizeAct; - QAction *fitToWindowAct; - - LammpsWrapper *lammps; + int current; + bool do_loop; + QStringList imagefiles; }; #endif