diff --git a/cmake/packaging/LAMMPS_DMG_Background.png b/cmake/packaging/LAMMPS_DMG_Background.png index 5ceb55c5e7..978b7d1987 100644 Binary files a/cmake/packaging/LAMMPS_DMG_Background.png and b/cmake/packaging/LAMMPS_DMG_Background.png differ diff --git a/cmake/packaging/MacOSXBundleInfo.plist.in b/cmake/packaging/MacOSXBundleInfo.plist.in index 33ce5a602b..bc08591e97 100644 --- a/cmake/packaging/MacOSXBundleInfo.plist.in +++ b/cmake/packaging/MacOSXBundleInfo.plist.in @@ -17,7 +17,7 @@ CFBundleLongVersionString ${MACOSX_BUNDLE_LONG_VERSION_STRING} CFBundleName - LAMMPS + LAMMPS_GUI CFBundlePackageType APPL CFBundleShortVersionString diff --git a/cmake/packaging/README.macos b/cmake/packaging/README.macos index 0325045983..d7583e7034 100644 --- a/cmake/packaging/README.macos +++ b/cmake/packaging/README.macos @@ -9,7 +9,7 @@ of the available packages. The following individual commands are included: binary2txt lammps-gui lmp msi2lmp phana stl_bin2txt -After copying the lammps-gui folder into your Applications folder, please follow +After copying the LAMMPS_GUI folder into your Applications folder, please follow these steps: 1. Open the Terminal app @@ -23,7 +23,7 @@ these steps: 3. Add the following lines to the end of the file, save it, and close the editor - LAMMPS_INSTALL_DIR=/Applications/LAMMPS.app/Contents + LAMMPS_INSTALL_DIR=/Applications/LAMMPS_GUI.app/Contents LAMMPS_POTENTIALS=${LAMMPS_INSTALL_DIR}/share/lammps/potentials LAMMPS_BENCH_DIR=${LAMMPS_INSTALL_DIR}/share/lammps/bench MSI2LMP_LIBRARY=${LAMMPS_INSTALL_DIR}/share/lammps/frc_files @@ -38,9 +38,9 @@ these steps: the changes from .zprofile automatically. Note: the above assumes you use the default shell (zsh) that comes with - MacOS. If you customized MacOS to use a different shell, you'll need to modify - that shell's init file (.cshrc, .bashrc, etc.) instead with appropiate commands - to modify the same environment variables. + MacOS. If you customized MacOS to use a different shell, you'll need to + modify that shell's init file (.cshrc, .bashrc, etc.) instead with + appropiate commands to modify the same environment variables. 5. Try running LAMMPS (which might fail, see step 7) @@ -50,10 +50,10 @@ these steps: lammps-gui ${LAMMPS_BENCH_DIR}/in.rhodo - Depending on the size and resolution of your screen, the fonts may - be too small to read. This can be adjusted by setting the environment - variable QT_FONT_DPI. The default value would be 72, so to increase - the fonts by a third one can add to the .zprofile file the line + Depending on the size and resolution of your screen, the fonts may be too + small to read. This can be adjusted by setting the environment variable + QT_FONT_DPI. The default value would be 72, so to increase the fonts by a + third, one can add to the .zprofile file the line export QT_FONT_DPI=96 @@ -61,9 +61,9 @@ these steps: 7. Give permission to execute the commands (lmp, lammps-gui, msi2lmp, binary2txt, phana, stl_bin2txt) - MacOS will likely block the initial run of the executables, since they - were downloaded from the internet and are missing a known signature from an - identified developer. Go to "Settings" and search for "Security settings". It - should display a message that an executable like "lmp" was blocked. Press + MacOS will likely block the initial run of the executables, since they were + downloaded from the internet and are missing a known signature from an + identified developer. Go to "Settings" and search for "Security settings". + It should display a message that an executable like "lmp" was blocked. Press "Open anyway", which might prompt you for your admin credentials. Afterwards "lmp" and the other executables should work as expected. diff --git a/cmake/packaging/build_linux_tgz.sh b/cmake/packaging/build_linux_tgz.sh index e0222858ce..48e3017f61 100755 --- a/cmake/packaging/build_linux_tgz.sh +++ b/cmake/packaging/build_linux_tgz.sh @@ -4,7 +4,7 @@ APP_NAME=lammps-gui DESTDIR=${PWD}/../LAMMPS_GUI echo "Delete old files, if they exist" -rm -rf ${DESTDIR} ../LAMMPS-Linux-amd64.tar.gz +rm -rf ${DESTDIR} ../LAMMPS_GUI-Linux-amd64.tar.gz echo "Create staging area for deployment and populate" DESTDIR=${DESTDIR} cmake --install . --prefix "/" @@ -69,7 +69,7 @@ do \ done pushd .. -tar -czvvf LAMMPS-Linux-amd64.tar.gz LAMMPS_GUI +tar -czvvf LAMMPS_GUI-Linux-amd64.tar.gz LAMMPS_GUI popd echo "Cleanup dir" diff --git a/cmake/packaging/build_macos_dmg.sh b/cmake/packaging/build_macos_dmg.sh index 5204e519c2..4da3e40eaf 100755 --- a/cmake/packaging/build_macos_dmg.sh +++ b/cmake/packaging/build_macos_dmg.sh @@ -3,7 +3,7 @@ APP_NAME=lammps-gui echo "Delete old files, if they exist" -rm -f ${APP_NAME}.dmg ${APP_NAME}-rw.dmg LAMMPS-macOS-multiarch.dmg +rm -f ${APP_NAME}.dmg ${APP_NAME}-rw.dmg LAMMPS_GUI-macOS-multiarch.dmg echo "Create initial dmg file with macdeployqt" macdeployqt lammps-gui.app -dmg @@ -22,8 +22,8 @@ ln -s /Applications . mv ${APP_NAME}.app/Contents/Resources/README.txt . mkdir .background mv ${APP_NAME}.app/Contents/Resources/LAMMPS_DMG_Background.png .background/background.png -mv ${APP_NAME}.app LAMMPS.app -cd LAMMPS.app/Contents +mv ${APP_NAME}.app LAMMPS_GUI.app +cd LAMMPS_GUI.app/Contents echo "Attach icons to LAMMPS console and GUI executables" echo "read 'icns' (-16455) \"Resources/lammps.icns\";" > icon.rsrc @@ -75,7 +75,7 @@ echo ' set statusbar visible to false set toolbar visible to false set the bounds to { 100, 40, 868, 640 } - set position of item "'LAMMPS'.app" to { 190, 216 } + set position of item "'LAMMPS_GUI'.app" to { 190, 216 } set position of item "Applications" to { 576, 216 } set position of item "README.txt" to { 190, 400 } end tell @@ -96,12 +96,12 @@ sync echo "Unmount modified disk image and convert to compressed read-only image" hdiutil detach "${DEVICE}" -hdiutil convert "${APP_NAME}-rw.dmg" -format UDZO -o "LAMMPS-macOS-multiarch.dmg" +hdiutil convert "${APP_NAME}-rw.dmg" -format UDZO -o "LAMMPS_GUI-macOS-multiarch.dmg" echo "Attach icon to .dmg file" echo "read 'icns' (-16455) \"lammps-gui.app/Contents/Resources/lammps.icns\";" > icon.rsrc -Rez -a icon.rsrc -o LAMMPS-macOS-multiarch.dmg -SetFile -a C LAMMPS-macOS-multiarch.dmg +Rez -a icon.rsrc -o LAMMPS_GUI-macOS-multiarch.dmg +SetFile -a C LAMMPS_GUI-macOS-multiarch.dmg rm icon.rsrc echo "Delete temporary disk images" diff --git a/cmake/packaging/build_windows_vs.cmake b/cmake/packaging/build_windows_vs.cmake index f051ff351f..bd55f3f442 100644 --- a/cmake/packaging/build_windows_vs.cmake +++ b/cmake/packaging/build_windows_vs.cmake @@ -1,7 +1,7 @@ # CMake script to be run post installation to build zipped package # clean up old zipfile and deployment tree -file(REMOVE LAMMPS-Win10-amd64.zip) +file(REMOVE LAMMPS_GUI-Win10-amd64.zip) file(REMOVE_RECURSE LAMMPS_GUI) file(RENAME ${INSTNAME} LAMMPS_GUI) @@ -22,7 +22,7 @@ execute_process(COMMAND cmd.exe /c qtdeploy.bat COMMAND_ECHO STDERR) file(REMOVE qtdeploy.bat) # create zip archive -file(WRITE makearchive.ps1 "Compress-Archive -Path LAMMPS_GUI -CompressionLevel Optimal -DestinationPath LAMMPS-Win10-amd64.zip") +file(WRITE makearchive.ps1 "Compress-Archive -Path LAMMPS_GUI -CompressionLevel Optimal -DestinationPath LAMMPS_GUI-Win10-amd64.zip") execute_process(COMMAND powershell -ExecutionPolicy Bypass -File makearchive.ps1) file(REMOVE makearchive.ps1) file(REMOVE_RECURSE LAMMPS_GUI) diff --git a/doc/src/Howto_lammps_gui.rst b/doc/src/Howto_lammps_gui.rst index 8ce827589c..aa8a224231 100644 --- a/doc/src/Howto_lammps_gui.rst +++ b/doc/src/Howto_lammps_gui.rst @@ -1,7 +1,7 @@ Using the LAMMPS GUI ==================== -This document describes **LAMMPS GUI version 1.3**. +This document describes **LAMMPS GUI version 1.4**. ----- @@ -168,7 +168,7 @@ Chart Window ------------ By default, when starting a run, a "Chart Window" will open that displays charts -of thermodynamic output of the LAMMPS calculation as shown below. +of the thermodynamic output of the LAMMPS calculation as shown below. .. image:: JPG/lammps-gui-chart.png :align: center @@ -191,6 +191,22 @@ unless the format, number, or names of output columns are changed or the current time step is reset with :doc:`reset_timestep ` or a :doc:`clear ` command is issued. +Image Slide Show +---------------- + +By default, in case the LAMMPS input contains a :doc:`dump image +` command, a "Slide Show" window will open which loads and +displays the images created by LAMMPS as they are written. + +.. image:: JPG/lammps-gui-slideshow.png + :align: center + :scale: 50% + +The various buttons at the bottom right of the window allow to either +single step through the list of images or play an animation (as a +continuous loop or once from first to last). It is also possible to +zoom in or zoom out. + Variable Info ------------- @@ -213,7 +229,7 @@ via the "Set Variables..." dialog from the ``Run`` menu. Viewing Snapshot Images ----------------------- -By selecting the ``View Image`` entry in the ``Run`` menu, by hitting +By selecting the ``Create Image`` entry in the ``Run`` menu, by hitting the `Ctrl-I` (`Command-I` on macOS) hotkey, or by clicking on the "palette" button in the status bar, LAMMPS GUI will issue a :doc:`write_dump image ` command and read the resulting @@ -347,7 +363,7 @@ not defined as far as the built-in parser can detect them. New rows for additional variables can be added through the ``Add Row`` button and existing rows may be deleted by clicking on the ``X`` icons on the right. -The ``View Image`` entry will send a :doc:`dump image ` +The ``Create Image`` entry will send a :doc:`dump image ` command to the LAMMPS instance, read the resulting file, and show it in an ``Image Viewer`` window. @@ -389,13 +405,13 @@ and looks of the LAMMPS GUI application. The settings are grouped and each group is displayed within a tab. .. |guiprefs1| image:: JPG/lammps-gui-prefs-general.png - :width: 25% + :width: 32% .. |guiprefs2| image:: JPG/lammps-gui-prefs-accel.png - :width: 25% + :width: 32% .. |guiprefs3| image:: JPG/lammps-gui-prefs-image.png - :width: 25% + :width: 32% |guiprefs1| |guiprefs2| |guiprefs3| @@ -405,7 +421,7 @@ General Settings: - *Echo input to log:* when checked, all input commands, including variable expansions, will be echoed to the log window. This is equivalent to using `-echo screen` at the command line. There is no - log *file* produced since it always uses `-log none`. + log *file* produced by default, since LAMMPS GUI uses `-log none`. - *Include citation details:* when checked full citation info will be included to the log window. This is equivalent to using `-cite screen` on the command line. @@ -414,6 +430,9 @@ General Settings: - *Show chart window by default:* when checked, the thermodynamic output of a LAMMPS run will be collected and displayed in a chart window as line graphs. +- *Show slide show window by default:* when checked, a slide show + window will be shown with images from a dump image command, if + present, in the LAMMPS input. - *Replace log window on new run:* when checked, an existing log window will be replaced on a new LAMMPS run, otherwise each run will create a new log window. @@ -481,54 +500,52 @@ instead of Ctrl/Control). - Function - Hotkey - Function - - Hotkey - - Function * - Ctrl+N - New File - Ctrl+Z - Undo edit - Ctrl+Enter - Run LAMMPS - - Ctrl+Shift+A - - About LAMMPS * - Ctrl+O - Open File - Ctrl+Shift+Z - Redo edit - Ctrl+/ - Stop Active Run - - Ctrl+Shift+H - - Quick Help * - CTRL+S - Save File - Ctrl+C - Copy text - Ctrl+Shift+V - Set Variables - - Ctrl+Shift+G - - LAMMPS GUI Howto * - Ctrl+Shift+S - Save File As - Ctrl+X - Cut text - Ctrl+I - Snapshot Image - - Ctrl+Shift+M - - LAMMPS Manual * - Ctrl+Q - Quit - Ctrl+V - Paste text - - Ctrl+P - - Preferences - - Ctrl+? - - Context Help + - Ctrl-L + - Slide Show * - Ctrl-W - Close Window - Ctrl-A - Select All - - Ctrl-Shift-Enter - - Run Current File + - Ctrl+P + - Preferences + * - Ctrl+Shift+A + - About LAMMPS + - Ctrl+Shift+H + - Quick Help + - Ctrl+Shift+G + - LAMMPS GUI Howto + * - Ctrl+Shift+M + - LAMMPS Manual + - Ctrl+? + - Context Help - Ctrl-Shift-W - Show Variables diff --git a/doc/src/JPG/lammps-gui-prefs-accel.png b/doc/src/JPG/lammps-gui-prefs-accel.png index 30368fe3e2..2477ba7183 100644 Binary files a/doc/src/JPG/lammps-gui-prefs-accel.png and b/doc/src/JPG/lammps-gui-prefs-accel.png differ diff --git a/doc/src/JPG/lammps-gui-prefs-general.png b/doc/src/JPG/lammps-gui-prefs-general.png index aefadaa70f..5849cdc267 100644 Binary files a/doc/src/JPG/lammps-gui-prefs-general.png and b/doc/src/JPG/lammps-gui-prefs-general.png differ diff --git a/doc/src/JPG/lammps-gui-prefs-image.png b/doc/src/JPG/lammps-gui-prefs-image.png index f45e5a173c..bbf3e8f777 100644 Binary files a/doc/src/JPG/lammps-gui-prefs-image.png and b/doc/src/JPG/lammps-gui-prefs-image.png differ diff --git a/doc/src/JPG/lammps-gui-slideshow.png b/doc/src/JPG/lammps-gui-slideshow.png new file mode 100644 index 0000000000..188a078429 Binary files /dev/null and b/doc/src/JPG/lammps-gui-slideshow.png differ diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 93d586f849..8f3486b1a7 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -1506,6 +1506,7 @@ Im imageint Imageint Imagemagick +imagename imd Impey impl diff --git a/src/dump_image.cpp b/src/dump_image.cpp index b1b91c6543..a20f33eca3 100644 --- a/src/dump_image.cpp +++ b/src/dump_image.cpp @@ -36,6 +36,8 @@ #include "memory.h" #include "modify.h" #include "molecule.h" +#include "output.h" +#include "thermo.h" #include "tokenizer.h" #include "update.h" #include "variable.h" @@ -77,18 +79,10 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : // set filetype based on filename suffix - int n = strlen(filename); - if (strlen(filename) > 4 && strcmp(&filename[n-4],".jpg") == 0) + if (utils::strmatch(filename, "\\.jpg$") || utils::strmatch(filename, "\\.JPG$") + || utils::strmatch(filename, "\\.jpeg$") || utils::strmatch(filename, "\\.JPEG$")) filetype = JPG; - else if (strlen(filename) > 4 && strcmp(&filename[n-4],".JPG") == 0) - filetype = JPG; - else if (strlen(filename) > 5 && strcmp(&filename[n-5],".jpeg") == 0) - filetype = JPG; - else if (strlen(filename) > 5 && strcmp(&filename[n-5],".JPEG") == 0) - filetype = JPG; - else if (strlen(filename) > 4 && strcmp(&filename[n-4],".png") == 0) - filetype = PNG; - else if (strlen(filename) > 4 && strcmp(&filename[n-4],".PNG") == 0) + else if (utils::strmatch(filename, "\\.png$") || utils::strmatch(filename, "\\.PNG$")) filetype = PNG; else filetype = PPM; @@ -481,6 +475,7 @@ DumpImage::DumpImage(LAMMPS *lmp, int narg, char **arg) : DumpImage::~DumpImage() { delete image; + output->thermo->set_image_fname(""); delete[] diamtype; delete[] diamelement; @@ -792,6 +787,12 @@ void DumpImage::write() if (multifile) { fclose(fp); fp = nullptr; + + // cache last dump image filename for access through library interface. + // update only *after* the file has been written so there will be no invalid read. + // have to recreate the substitution done within openfile(). + + output->thermo->set_image_fname(utils::star_subst(filename, update->ntimestep, padflag)); } } } diff --git a/src/dump_movie.cpp b/src/dump_movie.cpp index eff4455ef2..1a15774217 100644 --- a/src/dump_movie.cpp +++ b/src/dump_movie.cpp @@ -69,9 +69,8 @@ void DumpMovie::init_style() { // initialize image style circumventing multifile check - multifile = 1; + multifile_override = 1; DumpImage::init_style(); - multifile = 0; } /* ---------------------------------------------------------------------- */ diff --git a/src/library.cpp b/src/library.cpp index 7d4533deb6..6bac8ec3de 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -822,6 +822,10 @@ argument string. - line number (0-based) of current line in current file or buffer - pointer to int - no + * - imagename + - file name of the last :doc:`dump image ` file written + - pointer to 0-terminated const char array + - no * - step - timestep when the last thermo output was generated or -1 - pointer to bigint @@ -866,6 +870,9 @@ void *lammps_last_thermo(void *handle, const char *what, int index) } else if (strcmp(what, "line") == 0) { val = (void *) th->get_line(); + } else if (strcmp(what, "imagename") == 0) { + val = (void *) th->get_image_fname(); + } else if (strcmp(what, "step") == 0) { val = (void *) th->get_timestep(); @@ -2489,9 +2496,9 @@ string, otherwise 1. int lammps_variable_info(void *handle, int idx, char *buffer, int buf_size) { auto lmp = (LAMMPS *) handle; Info info(lmp); - auto varinfo = info.get_variable_info(idx); if ((idx >= 0) && (idx < lmp->input->variable->nvar)) { + auto varinfo = info.get_variable_info(idx); strncpy(buffer, varinfo.c_str(), buf_size); return 1; } diff --git a/src/read_restart.cpp b/src/read_restart.cpp index 6925bd6096..d0db7ad912 100644 --- a/src/read_restart.cpp +++ b/src/read_restart.cpp @@ -796,9 +796,12 @@ void ReadRestart::header() } else if (flag == ATOM_ID) { atom->tag_enable = read_int(); } else if (flag == ATOM_MAP_STYLE) { - atom->map_style = read_int(); + // we should be able to enable an atom map, even + // if the simulation in the restart didn't use one + int itmp = read_int(); + if (atom->map_user == Atom::MAP_NONE) atom->map_style = itmp; } else if (flag == ATOM_MAP_USER) { - atom->map_user = read_int(); + int itmp = read_int(); // ignored } else if (flag == ATOM_SORTFREQ) { atom->sortfreq = read_int(); } else if (flag == ATOM_SORTBIN) { diff --git a/src/thermo.cpp b/src/thermo.cpp index 801fd6ec6e..009c9cdf27 100644 --- a/src/thermo.cpp +++ b/src/thermo.cpp @@ -113,6 +113,7 @@ Thermo::Thermo(LAMMPS *_lmp, int narg, char **arg) : flushflag = 0; ntimestep = -1; nline = -1; + image_fname.clear(); // set style and corresponding lineflag // custom style builds its own line of keywords, including wildcard expansion diff --git a/src/thermo.h b/src/thermo.h index 9dca0f9f28..ad1e1c440d 100644 --- a/src/thermo.h +++ b/src/thermo.h @@ -42,14 +42,17 @@ class Thermo : protected Pointers { void compute(int); int evaluate_keyword(const std::string &, double *); - // for accessing cached thermo data - const int *get_nfield() const { return &nfield; } + // for accessing cached thermo and related data const int *get_line() const { return &nline; } + const char *get_image_fname() const { return image_fname.c_str(); } + + const int *get_nfield() const { return &nfield; } const bigint *get_timestep() const { return &ntimestep; } const std::vector &get_fields() const { return field_data; } const std::vector &get_keywords() const { return keyword; } void set_line(int _nline) { nline = _nline; } + void set_image_fname(const std::string &fname) { image_fname = fname; } private: int nfield, nfield_initial; @@ -75,6 +78,7 @@ class Thermo : protected Pointers { bigint natoms; bigint ntimestep; int nline; + std::string image_fname; // data used by routines that compute single values int ivalue; // integer value to print diff --git a/tools/lammps-gui/CMakeLists.txt b/tools/lammps-gui/CMakeLists.txt index 10ac72783f..4195c21406 100644 --- a/tools/lammps-gui/CMakeLists.txt +++ b/tools/lammps-gui/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(lammps-gui VERSION 1.3.1 LANGUAGES CXX) +project(lammps-gui VERSION 1.4.1 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) @@ -72,17 +72,17 @@ find_package(Qt5 5.12 REQUIRED COMPONENTS Widgets Charts) set(PROJECT_SOURCES main.cpp + lammpsgui.cpp + lammpsgui.h + lammpsgui.ui + chartviewer.cpp + chartviewer.h codeeditor.cpp codeeditor.h highlighter.cpp highlighter.h imageviewer.cpp imageviewer.h - chartviewer.cpp - chartviewer.h - lammpsgui.cpp - lammpsgui.h - lammpsgui.ui lammpsrunner.h lammpswrapper.cpp lammpswrapper.h @@ -93,6 +93,8 @@ set(PROJECT_SOURCES preferences.h setvariables.cpp setvariables.h + slideshow.h + slideshow.cpp stdcapture.cpp ${PLUGIN_LOADER_SRC} ${ICON_RC_FILE} diff --git a/tools/lammps-gui/TODO.md b/tools/lammps-gui/TODO.md index acd134e688..87abb63f5e 100644 --- a/tools/lammps-gui/TODO.md +++ b/tools/lammps-gui/TODO.md @@ -2,12 +2,8 @@ LAMMPS-GUI TODO list: # Short term goals -- rewrite syntax highlighting to be line oriented instead of word oriented. - handle first part of line based on regular expressions, then advance and only highlight strings and numbers. - handle "&" continuation and multiline strings with """ like C style comments in Qt docs example - add "syntax check" with enabled "-skiprun" flag - switch input file editor to read-only while loop is running -- add slideshow of images from dump image commands in the input # Long term ideas - rewrite entire application to build the App and its layout manually 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/highlighter.cpp b/tools/lammps-gui/highlighter.cpp index 7ed4844c69..5bf29ccd96 100644 --- a/tools/lammps-gui/highlighter.cpp +++ b/tools/lammps-gui/highlighter.cpp @@ -13,242 +13,220 @@ #include "highlighter.h" -Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent) +Highlighter::Highlighter(QTextDocument *parent) : + QSyntaxHighlighter(parent), + isLattice1(QStringLiteral("^\\s*(units|atom_style|change_box|dielectric|dimension)\\s+(\\S+)")), + isLattice2(QStringLiteral("^\\s*(lattice|region|create_box|create_atoms|delete_atoms|displace_" + "atoms)\\s+(\\S+)\\s+(\\S+)")), + isLattice3(QStringLiteral("^\\s*(boundary|replicate)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)")), + isOutput1(QStringLiteral("^\\s*(echo|log|write_data|write_coeff|write_restart|restart|info|" + "thermo|print|thermo_style|" + "timer|pair_write|bond_write|angle_write|dihedral_write)\\s+(\\S+)")), + isOutput2(QStringLiteral("^\\s*(write_dump|shell|thermo_modify)\\s+(\\S+)\\s+(\\S+)")), + isRead(QStringLiteral("^\\s*(include|read_restart|read_data|read_dump|molecule)")), + isStyle(QStringLiteral("^\\s*(fix|compute|dump)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)")), + isForce(QStringLiteral( + "^\\s*(pair_style|bond_style|angle_style|dihedral_style|improper_style|kspace_style|pair_" + "coeff|angle_coeff|bond_coeff|dihedral_coeff|improper_coeff)\\s+(\\S+)")), + isDefine(QStringLiteral("^\\s*(group|variable|python|set|group2ndx|ndx2group|kim|kim_query|mdi)" + "\\s+(\\S+)\\s+(\\S+)")), + isUndo(QStringLiteral("^\\s*(unfix|uncompute|undump|label|jump|next)\\s+(\\S+)")), + isParticle(QStringLiteral("^\\s*(pair_modify|mass|velocity|create_bonds|delete_" + "bonds|kspace_modify|labelmap|atom_modify)\\s+(\\S+)")), + isSetup(QStringLiteral( + "^\\s*(min_style|min_modify|run_style|timestep|neighbor|neigh_modify|suffix|special_bonds|" + "balance|box|clear|plugin|quit|comm_modify|comm_style|newton|package|partition|processors|" + "reset_atoms|reset_ids|reset_timestep|dump_modify|fix_modify|compute_modify)")), + isRun(QStringLiteral("^\\s*(minimize|minimize/kk|run|rerun|tad|neb|neb/spin|prd|server|temper/" + "npt|temper/grem|temper|message|hyper|dynamical_matrix|dynamical_matrix/" + "kk|third_order|third_order/kk|fitpod)")), + isVariable(QStringLiteral("\\s+(\\$[a-z]|\\${[^} ]+}|\\$\\(\\S+\\))")), + isReference( + QStringLiteral("\\s+(c_\\S+|C_\\S+|f_\\S+|F_\\S+|i_\\S+|i2_\\S+|d_\\S+|d2_\\S+|v_\\S+)")), + isNumber1(QStringLiteral("(^|\\s+)[0-9:*]+")), // integer and integer ranges + isNumber2(QStringLiteral("(^|\\s+)[0-9]+\\.[0-9]*[edED]?[-+]?[0-9]*")), // floating point 1 + isNumber3(QStringLiteral("(^|\\s+)[0-9]*\\.[0-9]+[edED]?[-+]?[0-9]*")), // floating point 2 + isNumber4(QStringLiteral("(^|\\s+)[0-9]+([edED][-+]?[0-9]+)?")), // floating point 3 + isSpecial( + QStringLiteral("(\\s\\+?-?INF|\\sEDGE|\\sNULL|\\sSELF|if\\s|then\\s|else\\s|elif\\s)")), + isContinue(QStringLiteral("&$")), isComment(QStringLiteral("#.*")), + isQuotedComment(QStringLiteral("(\".*#.*\"|'.*#.*')")), + isTriple(QStringLiteral("[^\"]*\"\"\"[^\"]*")), + isString(QStringLiteral("(\".+?\"|'.+?'|\"\"\".*\"\"\")")), in_triple(false) { - HighlightingRule rule; + formatNumber.setForeground(Qt::blue); + formatString.setForeground(Qt::darkGreen); + formatString.setFontWeight(QFont::Normal); + formatComment.setForeground(Qt::red); + formatSpecial.setForeground(Qt::darkMagenta); + formatSpecial.setFontWeight(QFont::Bold); + formatParticle.setForeground(Qt::darkRed); + formatParticle.setFontWeight(QFont::Bold); + formatRun.setForeground(Qt::darkBlue); + formatRun.setFontWeight(QFont::Bold); + formatVariable.setForeground(Qt::darkGray); + formatVariable.setFontWeight(QFont::Bold); - outputFormat.setForeground(Qt::darkYellow); - outputFormat.setFontWeight(QFont::Bold); - readFormat.setForeground(Qt::magenta); - readFormat.setFontWeight(QFont::Bold); - latticeFormat.setForeground(Qt::darkGreen); - latticeFormat.setFontWeight(QFont::Bold); - particleFormat.setForeground(Qt::darkRed); - particleFormat.setFontWeight(QFont::Bold); - setupFormat.setForeground(Qt::darkCyan); - setupFormat.setFontWeight(QFont::Bold); - runFormat.setForeground(Qt::darkBlue); - runFormat.setFontWeight(QFont::Bold); - defineFormat.setForeground(Qt::darkMagenta); - defineFormat.setFontWeight(QFont::Bold); - - numberFormat.setForeground(Qt::blue); - commentFormat.setForeground(Qt::red); - stringFormat.setForeground(Qt::darkGreen); - stringFormat.setFontWeight(QFont::Normal); - - const QString output_keywords[] = { - QStringLiteral("^\\s*log\\W"), QStringLiteral("^\\s*write_data\\W"), - QStringLiteral("^\\s*write_dump\\W"), QStringLiteral("^\\s*write_coeff\\W"), - QStringLiteral("^\\s*info\\W"), QStringLiteral("^\\s*shell\\W"), - QStringLiteral("^\\s*write_restart\\W"), QStringLiteral("^\\s*restart\\W"), - QStringLiteral("^\\s*dump\\W"), QStringLiteral("^\\s*undump\\W"), - QStringLiteral("^\\s*thermo\\W"), QStringLiteral("^\\s*thermo_modify\\W"), - QStringLiteral("^\\s*thermo_style\\W"), QStringLiteral("^\\s*print\\W"), - QStringLiteral("^\\s*timer\\W")}; - for (const QString &pattern : output_keywords) { - rule.pattern = QRegularExpression(pattern); - rule.format = outputFormat; - highlightingRules.append(rule); - } - - const QString read_keywords[] = { - QStringLiteral("^\\s*include\\W"), QStringLiteral("^\\s*read_restart\\W"), - QStringLiteral("^\\s*read_data\\W"), QStringLiteral("^\\s*read_dump\\W"), - QStringLiteral("^\\s*molecule\\W")}; - for (const QString &pattern : read_keywords) { - rule.pattern = QRegularExpression(pattern); - rule.format = readFormat; - highlightingRules.append(rule); - } - - const QString lattice_keywords[] = { - QStringLiteral("^\\s*boundary\\W"), QStringLiteral("^\\s*units\\W"), - QStringLiteral("^\\s*atom_style\\W"), QStringLiteral("^\\s*lattice\\W"), - QStringLiteral("^\\s*region\\W"), QStringLiteral("^\\s*create_box\\W"), - QStringLiteral("^\\s*create_atoms\\W"), QStringLiteral("^\\s*dielectric\\W"), - QStringLiteral("^\\s*delete_atoms\\W"), QStringLiteral("^\\s*displace_atoms\\W"), - QStringLiteral("^\\s*change_box\\W"), QStringLiteral("^\\s*dimension\\W"), - QStringLiteral("^\\s*replicate\\W")}; - for (const QString &pattern : lattice_keywords) { - rule.pattern = QRegularExpression(pattern); - rule.format = latticeFormat; - highlightingRules.append(rule); - } - - const QString particle_keywords[] = { - QStringLiteral("^\\s*pair_coeff\\W"), QStringLiteral("^\\s*pair_style\\W"), - QStringLiteral("^\\s*pair_modify\\W"), QStringLiteral("^\\s*pair_write\\W"), - QStringLiteral("^\\s*mass\\W"), QStringLiteral("^\\s*velocity\\W"), - QStringLiteral("^\\s*angle_coeff\\W"), QStringLiteral("^\\s*angle_style\\W"), - QStringLiteral("^\\s*angle_write\\W"), QStringLiteral("^\\s*atom_modify\\W"), - QStringLiteral("^\\s*atom_style\\W"), QStringLiteral("^\\s*bond_coeff\\W"), - QStringLiteral("^\\s*bond_style\\W"), QStringLiteral("^\\s*bond_write\\W"), - QStringLiteral("^\\s*create_bonds\\W"), QStringLiteral("^\\s*delete_bonds\\W"), - QStringLiteral("^\\s*kspace_style\\W"), QStringLiteral("^\\s*kspace_modify\\W"), - QStringLiteral("^\\s*dihedral_style\\W"), QStringLiteral("^\\s*dihedral_coeff\\W"), - QStringLiteral("^\\s*dihedral_write\\W"), QStringLiteral("^\\s*improper_style\\W"), - QStringLiteral("^\\s*improper_coeff\\W"), QStringLiteral("^\\s*labelmap\\W")}; - for (const QString &pattern : particle_keywords) { - rule.pattern = QRegularExpression(pattern); - rule.format = particleFormat; - highlightingRules.append(rule); - } - - const QString setup_keywords[] = {QStringLiteral("^\\s*min_style\\W"), - QStringLiteral("^\\s*min_modify\\W"), - QStringLiteral("^\\s*run_style\\W"), - QStringLiteral("^\\s*timestep\\W"), - QStringLiteral("^\\s*neighbor\\W"), - QStringLiteral("^\\s*neigh_modify\\W"), - QStringLiteral("^\\s*suffix\\W"), - QStringLiteral("^\\s*special_bonds\\W"), - QStringLiteral("^\\s*balance\\W"), - QStringLiteral("^\\s*box\\W"), - QStringLiteral("^\\s*clear\\W"), - QStringLiteral("^\\s*comm_modify\\W"), - QStringLiteral("^\\s*comm_style\\W"), - QStringLiteral("^\\s*newton\\W"), - QStringLiteral("^\\s*package\\W"), - QStringLiteral("^\\s*processors\\W"), - QStringLiteral("^\\s*reset_atoms\\W"), - QStringLiteral("^\\s*dump_modify\\W"), - QStringLiteral("^\\s*reset_ids\\W"), - QStringLiteral("^\\s*reset_timestep\\W"), - QStringLiteral("^\\s*label\\W"), - QStringLiteral("^\\s*jump\\W"), - QStringLiteral("^\\s*next\\W"), - QStringLiteral("^\\s*loop\\W") - - }; - for (const QString &pattern : setup_keywords) { - rule.pattern = QRegularExpression(pattern); - rule.format = setupFormat; - highlightingRules.append(rule); - } - - const QString run_keywords[] = {QStringLiteral("^\\s*minimize\\W"), - QStringLiteral("^\\s*minimize/kk\\W"), - QStringLiteral("^\\s*run\\W"), - QStringLiteral("^\\s*rerun\\W"), - QStringLiteral("^\\s*tad\\W"), - QStringLiteral("^\\s*neb\\W"), - QStringLiteral("^\\s*neb/spin\\W"), - QStringLiteral("^\\s*prd\\W"), - QStringLiteral("^\\s*quit\\W"), - QStringLiteral("^\\s*server\\W"), - QStringLiteral("^\\s*temper/npt\\W"), - QStringLiteral("^\\s*temper/grem\\W"), - QStringLiteral("^\\s*temper\\W"), - QStringLiteral("^\\s*message\\W"), - QStringLiteral("^\\s*hyper\\W"), - QStringLiteral("^\\s*dynamical_matrix\\W"), - QStringLiteral("^\\s*dynamical_matrix/kk\\W"), - QStringLiteral("^\\s*third_order\\W"), - QStringLiteral("^\\s*third_order/kk\\W"), - QStringLiteral("^\\s*fitpod\\W"), - QStringLiteral("^\\s*if\\W"), - QStringLiteral("^\\s*then\\W"), - QStringLiteral("^\\s*elif\\W"), - QStringLiteral("^\\s*else\\W")}; - for (const QString &pattern : run_keywords) { - rule.pattern = QRegularExpression(pattern); - rule.format = runFormat; - highlightingRules.append(rule); - } - - const QString define_keywords[] = {QStringLiteral("^\\s*variable\\W"), - QStringLiteral("^\\s*group\\W"), - QStringLiteral("^\\s*compute\\W"), - QStringLiteral("^\\s*python\\W"), - QStringLiteral("^\\s*set\\W"), - QStringLiteral("^\\s*uncompute\\W"), - QStringLiteral("^\\s*kim_query\\W"), - QStringLiteral("^\\s*kim\\W"), - QStringLiteral("^\\s*group2ndx\\W"), - QStringLiteral("^\\s*ndx2group\\W"), - QStringLiteral("^\\s*compute_modify\\W"), - QStringLiteral("^\\s*fix_modify\\W"), - QStringLiteral("^\\s*fix\\W"), - QStringLiteral("^\\s*unfix\\W"), - QStringLiteral("^\\s*INF\\W"), - QStringLiteral("^\\s*EDGE\\W"), - QStringLiteral("^\\s*NULL\\W"), - QStringLiteral("^\\s*&\\s*$"), - QStringLiteral("^\\s*mdi\\W")}; - for (const QString &pattern : define_keywords) { - rule.pattern = QRegularExpression(pattern); - rule.format = defineFormat; - highlightingRules.append(rule); - } - - const QString number_keywords[] = { - QStringLiteral("(^|\\s+)[0-9:*]+"), // integer and integer ranges - QStringLiteral("(^|\\s+)[0-9]+\\.[0-9]*[edED]?[-+]?[0-9]*"), // floating point 1 - QStringLiteral("(^|\\s+)[0-9]*\\.[0-9]+[edED]?[-+]?[0-9]*"), // floating point 2 - QStringLiteral("(^|\\s+)[0-9]+([edED][-+]?[0-9]+)?") // floating point 3 - }; - for (const QString &pattern : number_keywords) { - rule.pattern = QRegularExpression(pattern); - rule.format = numberFormat; - highlightingRules.append(rule); - } - - // comments, must come before strings but after other keywords. - rule.pattern = QRegularExpression("#.*"); - rule.format = commentFormat; - highlightingRules.append(rule); - - // strings, must come last so it overwrites other formatting - rule.pattern = QRegularExpression("(\".*\"|'.*')"); - rule.format = stringFormat; - highlightingRules.append(rule); + formatOutput.setForeground(Qt::darkYellow); + formatOutput.setFontWeight(QFont::Bold); + formatRead.setForeground(Qt::magenta); + formatRead.setFontWeight(QFont::Bold); + formatLattice.setForeground(Qt::darkGreen); + formatLattice.setFontWeight(QFont::Bold); + formatSetup.setForeground(Qt::darkCyan); + formatSetup.setFontWeight(QFont::Bold); } void Highlighter::highlightBlock(const QString &text) { - // clang-format off - auto style = QRegularExpression("^(fix|compute|dump|set)\\s+(\\w+)\\s+(\\S+)\\s+(\\S+)").match(text); - auto force = QRegularExpression("^(atom_style|pair_style|bond_style|angle_style|dihedral_style|improper_style|kspace_style)\\s+(\\S+)").match(text); - auto defs = QRegularExpression("^(group|variable)\\s+(\\S+)\\s+(\\S+)").match(text); - auto undo = QRegularExpression("^(unfix|uncompute|undump)\\s+(\\w+)").match(text); - // clang-format on - bool do_style = true; - bool do_force = true; - bool do_defs = true; - bool do_undo = true; - for (const HighlightingRule &rule : qAsConst(highlightingRules)) { - QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text); - while (matchIterator.hasNext()) { - QRegularExpressionMatch match = matchIterator.next(); - // special treatment for fix/compute/dump styles etc. - if (style.hasMatch() && do_style) { - setFormat(style.capturedStart(1), style.capturedLength(1), defineFormat); - setFormat(style.capturedStart(2), style.capturedLength(2), numberFormat); - setFormat(style.capturedStart(3), style.capturedLength(3), stringFormat); - setFormat(style.capturedStart(4), style.capturedLength(4), runFormat); - do_style = false; - // special treatment for force styles styles - } else if (force.hasMatch() && do_force) { - setFormat(force.capturedStart(1), force.capturedLength(1), particleFormat); - setFormat(force.capturedStart(2), force.capturedLength(2), runFormat); - do_force = false; - // special treatment for undo commands - } else if (undo.hasMatch() && do_undo) { - setFormat(undo.capturedStart(1), undo.capturedLength(1), defineFormat); - setFormat(undo.capturedStart(2), undo.capturedLength(2), stringFormat); - do_undo = false; - // special treatment for some definitions - } else if (defs.hasMatch() && do_defs) { - setFormat(defs.capturedStart(1), defs.capturedLength(1), particleFormat); - setFormat(defs.capturedStart(2), defs.capturedLength(2), stringFormat); - setFormat(defs.capturedStart(3), defs.capturedLength(3), runFormat); - do_defs = false; - } else { - setFormat(match.capturedStart(), match.capturedLength(), rule.format); - } + // nothing to do for empty lines + if (text.isEmpty()) return; + + auto match = isLattice1.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatLattice); + setFormat(match.capturedStart(2), match.capturedLength(2), formatRun); + } + + match = isLattice2.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatLattice); + setFormat(match.capturedStart(2), match.capturedLength(2), formatString); + setFormat(match.capturedStart(3), match.capturedLength(3), formatRun); + } + + match = isLattice3.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatLattice); + setFormat(match.capturedStart(2), match.capturedLength(2), formatString); + setFormat(match.capturedStart(3), match.capturedLength(3), formatString); + setFormat(match.capturedStart(4), match.capturedLength(4), formatString); + } + + match = isOutput1.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatOutput); + setFormat(match.capturedStart(2), match.capturedLength(2), formatString); + } + + match = isOutput2.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatOutput); + setFormat(match.capturedStart(2), match.capturedLength(2), formatString); + setFormat(match.capturedStart(3), match.capturedLength(3), formatRun); + } + + match = isRead.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatRead); + setFormat(match.capturedStart(2), match.capturedLength(2), formatString); + } + + match = isStyle.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatParticle); + setFormat(match.capturedStart(2), match.capturedLength(2), formatNumber); + setFormat(match.capturedStart(3), match.capturedLength(3), formatString); + setFormat(match.capturedStart(4), match.capturedLength(4), formatRun); + } + + match = isForce.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatParticle); + setFormat(match.capturedStart(2), match.capturedLength(2), formatRun); + } + + match = isUndo.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatSpecial); + setFormat(match.capturedStart(2), match.capturedLength(2), formatString); + } + + match = isDefine.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatParticle); + setFormat(match.capturedStart(2), match.capturedLength(2), formatString); + setFormat(match.capturedStart(3), match.capturedLength(3), formatRun); + } + + match = isParticle.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatParticle); + setFormat(match.capturedStart(2), match.capturedLength(2), formatString); + } + + match = isSetup.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatSetup); + } + + match = isRun.match(text); + if (match.hasMatch()) { + setFormat(match.capturedStart(1), match.capturedLength(1), formatRun); + } + + // numbers + QRegularExpression numbers[] = {isNumber1, isNumber2, isNumber3, isNumber4}; + for (auto &number : numbers) { + auto num = number.globalMatch(text); + while (num.hasNext()) { + auto match = num.next(); + setFormat(match.capturedStart(), match.capturedLength(), formatNumber); } } + + // variables + auto vars = isVariable.globalMatch(text); + while (vars.hasNext()) { + auto match = vars.next(); + setFormat(match.capturedStart(), match.capturedLength(), formatVariable); + } + + // references + auto refs = isReference.globalMatch(text); + while (refs.hasNext()) { + auto match = refs.next(); + setFormat(match.capturedStart(), match.capturedLength(), formatVariable); + } + + // continuation character + auto multiline = isContinue.match(text); + if (multiline.hasMatch()) + setFormat(multiline.capturedStart(0), multiline.capturedLength(0), formatSpecial); + + // special keywords + auto special = isSpecial.globalMatch(text); + while (special.hasNext()) { + auto match = special.next(); + setFormat(match.capturedStart(), match.capturedLength(), formatSpecial); + } + + // comments, must come before strings but after other keywords. + auto comment = isComment.match(text); + if (comment.hasMatch() && !isQuotedComment.match(text).hasMatch() && !in_triple) { + setFormat(comment.capturedStart(0), comment.capturedLength(0), formatComment); + return; + } + + // strings, must come last so they can overwrite other formatting + auto string = isString.globalMatch(text); + while (string.hasNext()) { + auto match = string.next(); + setFormat(match.capturedStart(), match.capturedLength(), formatString); + } + + auto triple = isTriple.match(text); + if (triple.hasMatch()) { + if (in_triple) { + in_triple = false; + setFormat(0, triple.capturedStart(0) + triple.capturedLength(0), formatString); + } else { + in_triple = true; + setFormat(triple.capturedStart(0), -1, formatString); + } + } else { + if (in_triple) setFormat(0, text.size(), formatString); + } } // Local Variables: // c-basic-offset: 4 diff --git a/tools/lammps-gui/highlighter.h b/tools/lammps-gui/highlighter.h index 234333924e..57ca34f23b 100644 --- a/tools/lammps-gui/highlighter.h +++ b/tools/lammps-gui/highlighter.h @@ -28,23 +28,27 @@ protected: void highlightBlock(const QString &text) override; private: - struct HighlightingRule { - QRegularExpression pattern; - QTextCharFormat format; - }; - QVector highlightingRules; + QRegularExpression isLattice1, isLattice2, isLattice3; + QRegularExpression isOutput1, isOutput2, isRead; + QTextCharFormat formatOutput, formatRead, formatLattice, formatSetup; + QRegularExpression isStyle, isForce, isDefine, isUndo; + QRegularExpression isParticle, isSetup, isRun; + QTextCharFormat formatParticle, formatRun, formatDefine; + QRegularExpression isVariable, isReference; + QTextCharFormat formatVariable; + QRegularExpression isNumber1, isNumber2, isNumber3, isNumber4; + QTextCharFormat formatNumber; + QRegularExpression isSpecial, isContinue; + QTextCharFormat formatSpecial; + QRegularExpression isComment; + QRegularExpression isQuotedComment; + QTextCharFormat formatComment; + QRegularExpression isTriple; + QRegularExpression isString; + QTextCharFormat formatString; - QTextCharFormat outputFormat; - QTextCharFormat readFormat; - QTextCharFormat latticeFormat; - QTextCharFormat particleFormat; - QTextCharFormat setupFormat; - QTextCharFormat runFormat; - QTextCharFormat defineFormat; - - QTextCharFormat numberFormat; - QTextCharFormat stringFormat; - QTextCharFormat commentFormat; + int in_triple; + int startIndex; }; #endif // Local Variables: diff --git a/tools/lammps-gui/image-x-generic.png b/tools/lammps-gui/image-x-generic.png new file mode 100644 index 0000000000..1fe112632a Binary files /dev/null and b/tools/lammps-gui/image-x-generic.png differ diff --git a/tools/lammps-gui/imageviewer.cpp b/tools/lammps-gui/imageviewer.cpp index 5afb02e5b1..139d73cb38 100644 --- a/tools/lammps-gui/imageviewer.cpp +++ b/tools/lammps-gui/imageviewer.cpp @@ -103,8 +103,10 @@ ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidge connect(xval, &QAbstractSpinBox::editingFinished, this, &ImageViewer::edit_size); connect(yval, &QAbstractSpinBox::editingFinished, this, &ImageViewer::edit_size); + // workaround for incorrect highlight bug on macOS auto *dummy = new QPushButton(QIcon(), ""); dummy->hide(); + auto *dossao = new QPushButton(QIcon(":/hd-img.png"), ""); dossao->setCheckable(true); dossao->setToolTip("Toggle SSAO rendering"); @@ -190,6 +192,7 @@ ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidge mainLayout->addLayout(menuLayout); mainLayout->addWidget(scrollArea); mainLayout->addWidget(buttonBox); + setWindowIcon(QIcon(":/lammps-icon-128x128.png")); setWindowTitle(QString("Image Viewer: ") + QFileInfo(fileName).fileName()); createActions(); @@ -207,9 +210,7 @@ ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidge resize(image.width() + 20, image.height() + 50); scrollArea->setVisible(true); - fitToWindowAct->setEnabled(true); updateActions(); - if (!fitToWindowAct->isChecked()) imageLabel->adjustSize(); setLayout(mainLayout); } @@ -464,30 +465,6 @@ void ImageViewer::saveAs() void ImageViewer::copy() {} -void ImageViewer::zoomIn() -{ - scaleImage(1.25); -} - -void ImageViewer::zoomOut() -{ - scaleImage(0.8); -} - -void ImageViewer::normalSize() -{ - imageLabel->adjustSize(); - scaleFactor = 1.0; -} - -void ImageViewer::fitToWindow() -{ - bool fitToWindow = fitToWindowAct->isChecked(); - scrollArea->setWidgetResizable(fitToWindow); - if (!fitToWindow) normalSize(); - updateActions(); -} - void ImageViewer::saveFile(const QString &fileName) { if (!fileName.isEmpty()) image.save(fileName); @@ -509,39 +486,12 @@ void ImageViewer::createActions() 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, &ImageViewer::zoomIn); - zoomInAct->setShortcut(QKeySequence::ZoomIn); - zoomInAct->setIcon(QIcon(":/gtk-zoom-in.png")); - zoomInAct->setEnabled(false); - - zoomOutAct = viewMenu->addAction("Image Zoom &Out (25%)", this, &ImageViewer::zoomOut); - zoomOutAct->setShortcut(QKeySequence::ZoomOut); - zoomOutAct->setIcon(QIcon(":/gtk-zoom-out.png")); - zoomOutAct->setEnabled(false); - - normalSizeAct = viewMenu->addAction("&Reset Image Size", this, &ImageViewer::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, &ImageViewer::fitToWindow); - fitToWindowAct->setEnabled(false); - fitToWindowAct->setCheckable(true); - fitToWindowAct->setShortcut(QKeySequence::fromString("Ctrl+=")); } void ImageViewer::updateActions() { saveAsAct->setEnabled(!image.isNull()); copyAct->setEnabled(!image.isNull()); - zoomInAct->setEnabled(!fitToWindowAct->isChecked()); - zoomOutAct->setEnabled(!fitToWindowAct->isChecked()); - normalSizeAct->setEnabled(!fitToWindowAct->isChecked()); } void ImageViewer::scaleImage(double factor) @@ -555,8 +505,6 @@ void ImageViewer::scaleImage(double factor) adjustScrollBar(scrollArea->horizontalScrollBar(), factor); adjustScrollBar(scrollArea->verticalScrollBar(), factor); - zoomInAct->setEnabled(scaleFactor < 3.0); - zoomOutAct->setEnabled(scaleFactor > 0.333); } void ImageViewer::adjustScrollBar(QScrollBar *scrollBar, double factor) diff --git a/tools/lammps-gui/imageviewer.h b/tools/lammps-gui/imageviewer.h index 3dbf3df324..013a90249f 100644 --- a/tools/lammps-gui/imageviewer.h +++ b/tools/lammps-gui/imageviewer.h @@ -40,10 +40,6 @@ public: private slots: void saveAs(); void copy(); - void zoomIn(); - void zoomOut(); - void normalSize(); - void fitToWindow(); void edit_size(); void reset_view(); diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index d5fe1d043a..7966db61af 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -20,6 +20,7 @@ #include "logwindow.h" #include "preferences.h" #include "setvariables.h" +#include "slideshow.h" #include "stdcapture.h" #include "ui_lammpsgui.h" @@ -96,8 +97,8 @@ static bool has_exe(const QString &exe) LammpsGui::LammpsGui(QWidget *parent, const char *filename) : QMainWindow(parent), ui(new Ui::LammpsGui), highlighter(nullptr), capturer(nullptr), status(nullptr), logwindow(nullptr), imagewindow(nullptr), chartwindow(nullptr), - logupdater(nullptr), dirstatus(nullptr), progress(nullptr), prefdialog(nullptr), - lammpsstatus(nullptr), varwindow(nullptr) + slideshow(nullptr), logupdater(nullptr), dirstatus(nullptr), progress(nullptr), + prefdialog(nullptr), lammpsstatus(nullptr), varwindow(nullptr) { // enforce using the plain ASCII C locale within the GUI. QLocale::setDefault(QLocale("C")); @@ -239,6 +240,7 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) : connect(ui->actionView_Log_Window, &QAction::triggered, this, &LammpsGui::view_log); connect(ui->actionView_Graph_Window, &QAction::triggered, this, &LammpsGui::view_chart); connect(ui->actionView_Image_Window, &QAction::triggered, this, &LammpsGui::view_image); + connect(ui->actionView_Slide_Show, &QAction::triggered, this, &LammpsGui::view_slides); connect(ui->actionView_Variable_Window, &QAction::triggered, this, &LammpsGui::view_variables); connect(ui->action_1, &QAction::triggered, this, &LammpsGui::open_recent); connect(ui->action_2, &QAction::triggered, this, &LammpsGui::open_recent); @@ -307,6 +309,7 @@ LammpsGui::~LammpsGui() delete chartwindow; delete dirstatus; delete varwindow; + delete slideshow; } void LammpsGui::new_document() @@ -545,6 +548,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(); } @@ -710,20 +717,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 @@ -768,6 +780,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() @@ -956,6 +984,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); @@ -982,6 +1016,15 @@ void LammpsGui::render_image() imagewindow->show(); } +void LammpsGui::view_slides() +{ + if (!slideshow) slideshow = new SlideShow(current_file); + if (slideshow->isVisible()) + slideshow->hide(); + else + slideshow->show(); +} + void LammpsGui::view_chart() { QSettings settings; @@ -1068,6 +1111,7 @@ void LammpsGui::about() QMessageBox msg; msg.setWindowTitle("About LAMMPS"); + msg.setWindowIcon(QIcon(":/lammps-icon-128x128.png")); msg.setText(version.c_str()); msg.setInformativeText(info.c_str()); msg.setIconPixmap(QPixmap(":/lammps-icon-128x128.png").scaled(64, 64)); @@ -1087,6 +1131,7 @@ void LammpsGui::help() { QMessageBox msg; msg.setWindowTitle("LAMMPS-GUI Quick Help"); + msg.setWindowIcon(QIcon(":/lammps-icon-128x128.png")); msg.setText("
This is LAMMPS-GUI version " LAMMPS_GUI_VERSION "
"); msg.setInformativeText("

LAMMPS GUI is a graphical text editor that is linked to the LAMMPS " "library and thus can run LAMMPS directly using the contents of the " diff --git a/tools/lammps-gui/lammpsgui.h b/tools/lammps-gui/lammpsgui.h index aeda618443..6d80b0aee6 100644 --- a/tools/lammps-gui/lammpsgui.h +++ b/tools/lammps-gui/lammpsgui.h @@ -45,6 +45,7 @@ class StdCapture; class Preferences; class ImageViewer; class ChartWindow; +class SlideShow; class LammpsGui : public QMainWindow { Q_OBJECT @@ -84,6 +85,7 @@ private slots: void stop_run(); void edit_variables(); void render_image(); + void view_slides(); void view_image(); void view_chart(); void view_log(); @@ -107,6 +109,7 @@ private: LogWindow *logwindow; ImageViewer *imagewindow; ChartWindow *chartwindow; + SlideShow *slideshow; QTimer *logupdater; QLabel *dirstatus; QProgressBar *progress; diff --git a/tools/lammps-gui/lammpsgui.qrc b/tools/lammps-gui/lammpsgui.qrc index 31f997f05e..a27d6abbea 100644 --- a/tools/lammps-gui/lammpsgui.qrc +++ b/tools/lammps-gui/lammpsgui.qrc @@ -1,49 +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 - 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 9ef6525f9b..77257b23c2 100644 --- a/tools/lammps-gui/lammpsgui.ui +++ b/tools/lammps-gui/lammpsgui.ui @@ -93,6 +93,7 @@ + @@ -250,7 +251,7 @@ - View &Image + Create &Image Ctrl+I @@ -352,6 +353,17 @@ Ctrl+Shift+C + + + + + + &Slide Show Window + + + 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..e4fc276912 100644 --- a/tools/lammps-gui/preferences.cpp +++ b/tools/lammps-gui/preferences.cpp @@ -78,6 +78,7 @@ Preferences::Preferences(LammpsWrapper *_lammps, QWidget *parent) : layout->addWidget(tabWidget); layout->addWidget(buttonBox); setLayout(layout); + setWindowIcon(QIcon(":/lammps-icon-128x128.png")); setWindowTitle("LAMMPS-GUI - Preferences"); resize(500, 400); } @@ -161,6 +162,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 +195,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 window 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 +238,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/setvariables.cpp b/tools/lammps-gui/setvariables.cpp index a63832fc57..fbbacb70bd 100644 --- a/tools/lammps-gui/setvariables.cpp +++ b/tools/lammps-gui/setvariables.cpp @@ -55,6 +55,7 @@ SetVariables::SetVariables(QList> &_vars, QWidget *paren layout->addWidget(buttonBox); setLayout(layout); + setWindowIcon(QIcon(":/lammps-icon-128x128.png")); setWindowTitle("LAMMPS-GUI - Set Variables"); resize(300, 200); } diff --git a/tools/lammps-gui/slideshow.cpp b/tools/lammps-gui/slideshow.cpp new file mode 100644 index 0000000000..a42a67f8a2 --- /dev/null +++ b/tools/lammps-gui/slideshow.cpp @@ -0,0 +1,280 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "slideshow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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->setScaledContents(false); + imageLabel->setMinimumSize(100, 100); + + imageName->setFrameStyle(QFrame::Raised); + imageName->setFrameShape(QFrame::Panel); + imageName->setAlignment(Qt::AlignCenter); + imageName->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + 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); + + auto *mainLayout = new QVBoxLayout; + auto *navLayout = new QHBoxLayout; + auto *botLayout = new QHBoxLayout; + + // workaround for incorrect highlight bug on macOS + auto *dummy = new QPushButton(QIcon(), ""); + dummy->hide(); + + 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->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum)); + navLayout->addWidget(dummy); + 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); + + botLayout->addWidget(imageName); + botLayout->addWidget(buttonBox); + botLayout->setStretch(0, 3); + mainLayout->addLayout(botLayout); + + setWindowIcon(QIcon(":/lammps-icon-128x128.png")); + setWindowTitle(QString("LAMMPS-GUI - Slide Show: ") + QFileInfo(fileName).fileName()); + + imagefiles.clear(); + scaleFactor = 1.0; + current = 0; + + auto maxsize = QGuiApplication::primaryScreen()->availableSize() * 4 / 5; + maxheight = maxsize.height(); + maxwidth = maxsize.width(); + + setLayout(mainLayout); +} + +void SlideShow::add_image(const QString &filename) +{ + if (!imagefiles.contains(filename)) { + int lastidx = imagefiles.size(); + imagefiles.append(filename); + loadImage(lastidx); + } +} + +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(QString(" Image %1 / %2 : %3 ") + .arg(idx + 1) + .arg(imagefiles.size()) + .arg(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.1); +} + +void SlideShow::zoomOut() +{ + scaleImage(0.9); +} + +void SlideShow::normalSize() +{ + scaleFactor = 1.0; + scaleImage(1.0); +} + +void SlideShow::scaleImage(double factor) +{ + // compute maxfactor so the image is not scaled beyond 80 of width or height of screen + double maxfactor = 10.0; + maxfactor = qMin((double)maxheight / (double)image.height(), maxfactor); + maxfactor = qMin((double)maxwidth / (double)image.width(), maxfactor); + + if (factor > maxfactor) factor = maxfactor; + scaleFactor *= factor; + if (scaleFactor < 0.25) scaleFactor = 0.25; + + loadImage(current); +} + +// Local Variables: +// c-basic-offset: 4 +// End: diff --git a/tools/lammps-gui/slideshow.h b/tools/lammps-gui/slideshow.h new file mode 100644 index 0000000000..fe357ec564 --- /dev/null +++ b/tools/lammps-gui/slideshow.h @@ -0,0 +1,65 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef SLIDESHOW_H +#define SLIDESHOW_H + +#include +#include +#include +#include + +class QDialogButtonBox; +class QLabel; +class QTimer; + +class SlideShow : public QDialog { + Q_OBJECT + +public: + explicit SlideShow(const QString &fileName, QWidget *parent = nullptr); + void add_image(const QString &filename); + void clear(); + +private slots: + void first(); + void last(); + void next(); + void prev(); + void play(); + void loop(); + void zoomIn(); + void zoomOut(); + void normalSize(); + +private: + void scaleImage(double factor); + void loadImage(int idx); + +private: + QImage image; + QTimer *playtimer; + QLabel *imageLabel, *imageName; + QDialogButtonBox *buttonBox; + double scaleFactor = 1.0; + + int current; + int maxwidth, maxheight; + bool do_loop; + QStringList imagefiles; +}; +#endif + +// Local Variables: +// c-basic-offset: 4 +// End: diff --git a/unittest/formats/test_atom_styles.cpp b/unittest/formats/test_atom_styles.cpp index 689a9f4735..4ad571f9fa 100644 --- a/unittest/formats/test_atom_styles.cpp +++ b/unittest/formats/test_atom_styles.cpp @@ -533,7 +533,7 @@ TEST_F(AtomStyleTest, atomic) ASSERT_NE(lmp->atom->mass_setflag, nullptr); ASSERT_NE(lmp->atom->sametag, nullptr); ASSERT_EQ(lmp->atom->map_style, Atom::MAP_HASH); - ASSERT_EQ(lmp->atom->map_user, 2); + ASSERT_EQ(lmp->atom->map_user, Atom::MAP_HASH); ASSERT_EQ(lmp->atom->map_tag_max, 4); BEGIN_HIDE_OUTPUT(); command("pair_coeff * *"); @@ -587,7 +587,7 @@ TEST_F(AtomStyleTest, atomic) ASSERT_EQ(lmp->atom->mass_setflag[1], 1); ASSERT_EQ(lmp->atom->mass_setflag[2], 1); ASSERT_EQ(lmp->atom->map_style, Atom::MAP_ARRAY); - ASSERT_EQ(lmp->atom->map_user, 1); + ASSERT_EQ(lmp->atom->map_user, Atom::MAP_ARRAY); ASSERT_EQ(lmp->atom->map_tag_max, 4); ASSERT_EQ(lmp->atom->tag_consecutive(), 1); @@ -597,6 +597,7 @@ TEST_F(AtomStyleTest, atomic) command("delete_atoms group two compress no"); command("write_restart test_atom_styles.restart"); command("clear"); + command("atom_modify map hash"); command("read_restart test_atom_styles.restart"); END_HIDE_OUTPUT(); ASSERT_THAT(std::string(lmp->atom->atom_style), Eq("atomic")); @@ -629,8 +630,8 @@ TEST_F(AtomStyleTest, atomic) EXPECT_NEAR(lmp->atom->mass[2], 2.4, EPSILON); ASSERT_EQ(lmp->atom->mass_setflag[1], 1); ASSERT_EQ(lmp->atom->mass_setflag[2], 1); - ASSERT_EQ(lmp->atom->map_style, Atom::MAP_ARRAY); - ASSERT_EQ(lmp->atom->map_user, 1); + ASSERT_EQ(lmp->atom->map_style, Atom::MAP_HASH); + ASSERT_EQ(lmp->atom->map_user, Atom::MAP_HASH); ASSERT_EQ(lmp->atom->map_tag_max, 3); BEGIN_HIDE_OUTPUT(); command("reset_atom_ids"); @@ -4561,8 +4562,8 @@ TEST_F(AtomStyleTest, property_atom) expected.atom_style = "atomic"; expected.molecular = Atom::ATOMIC; expected.tag_enable = 1; - expected.map_style = 1; - expected.map_user = 1; + expected.map_style = Atom::MAP_ARRAY; + expected.map_user = Atom::MAP_ARRAY; expected.has_type = true; expected.has_image = true; expected.has_mask = true; @@ -4663,7 +4664,7 @@ TEST_F(AtomStyleTest, property_atom) ASSERT_NE(lmp->atom->sametag, nullptr); ASSERT_EQ(lmp->atom->tag_consecutive(), 1); ASSERT_EQ(lmp->atom->map_style, Atom::MAP_ARRAY); - ASSERT_EQ(lmp->atom->map_user, 1); + ASSERT_EQ(lmp->atom->map_user, Atom::MAP_ARRAY); ASSERT_EQ(lmp->atom->map_tag_max, 4); auto x = lmp->atom->x; @@ -4751,6 +4752,7 @@ TEST_F(AtomStyleTest, property_atom) expected.has_ianame = true; expected.has_daname = true; expected.nextra_store = 12; + expected.map_user = Atom::MAP_NONE; ASSERT_ATOM_STATE_EQ(lmp->atom, expected); ASSERT_NE(lmp->atom->avec, nullptr);