feat: cli options

This commit is contained in:
2026-01-15 03:56:26 +01:00
parent 92ba8738fe
commit f71273a7df
10 changed files with 268 additions and 168 deletions
+1
View File
@@ -1,5 +1,6 @@
.cache .cache
.vscode .vscode
.qtcreator
build-dbg build-dbg
build build
*.log *.log
+14 -10
View File
@@ -1,6 +1,10 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(wallpaper_carousel VERSION 0.1 LANGUAGES CXX) project(Wallpaper_Carousel VERSION 1.0.0 LANGUAGES CXX)
set(EXECUTABLE_NAME "wallpaper-carousel")
configure_file(src/version.h.in ${CMAKE_BINARY_DIR}/generated/version.h)
set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
@@ -27,7 +31,7 @@ set(PROJECT_SOURCES
) )
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(wallpaper-carousel qt_add_executable(${EXECUTABLE_NAME}
MANUAL_FINALIZATION MANUAL_FINALIZATION
${PROJECT_SOURCES} ${PROJECT_SOURCES}
src/utils.h src/utils.h
@@ -44,22 +48,22 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation # For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else() else()
if(ANDROID) if(ANDROID)
add_library(wallpaper-carousel SHARED add_library(${EXECUTABLE_NAME} SHARED
${PROJECT_SOURCES} ${PROJECT_SOURCES}
) )
# Define properties for Android with Qt 5 after find_package() calls as: # Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") # set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else() else()
add_executable(wallpaper-carousel add_executable(${EXECUTABLE_NAME}
${PROJECT_SOURCES} ${PROJECT_SOURCES}
) )
endif() endif()
endif() endif()
target_link_libraries(wallpaper-carousel PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) target_link_libraries(${EXECUTABLE_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_include_directories(wallpaper-carousel PRIVATE src) target_include_directories(${EXECUTABLE_NAME} PRIVATE src ${CMAKE_BINARY_DIR}/generated)
# if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") # if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug")
# target_compile_definitions(wallpaper_chooser PRIVATE # target_compile_definitions(wallpaper_chooser PRIVATE
@@ -74,7 +78,7 @@ if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.wallpaper_chooser) set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.wallpaper_chooser)
endif() endif()
set_target_properties(wallpaper-carousel PROPERTIES set_target_properties(${EXECUTABLE_NAME} PROPERTIES
${BUNDLE_ID_OPTION} ${BUNDLE_ID_OPTION}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
@@ -83,16 +87,16 @@ set_target_properties(wallpaper-carousel PROPERTIES
) )
include(GNUInstallDirs) include(GNUInstallDirs)
install(TARGETS wallpaper-carousel install(TARGETS ${EXECUTABLE_NAME}
BUNDLE DESTINATION . BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
) )
install(FILES ${CMAKE_CURRENT_LIST_DIR}/app/wallpaper-carousel.desktop install(FILES ${CMAKE_CURRENT_LIST_DIR}/app/${EXECUTABLE_NAME}.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications
) )
if(QT_VERSION_MAJOR EQUAL 6) if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(wallpaper-carousel) qt_finalize_executable(${EXECUTABLE_NAME})
endif() endif()
+4 -9
View File
@@ -1,4 +1,4 @@
#!/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
@@ -9,15 +9,10 @@ path="$(dirname "$(readlink -f "$0")")"
cmake -S "$path/.." -B "$path/../build" \ cmake -S "$path/.." -B "$path/../build" \
-DCMAKE_INSTALL_PREFIX="$prefix" -DCMAKE_INSTALL_PREFIX="$prefix"
cmake --build "$path/../build" cmake --build "$path/../build" --config Release -- -j"$(nproc)"
if [ ! -w "$prefix" ] && [ "$(id -u)" -ne 0 ]; then if [ ! -w "$prefix" ] && [ "$(id -u)" -ne 0 ]; then
sudo cmake --install "$path/../build" sudo cmake --install "$path/../build" --config Release
else else
cmake --install "$path/../build" cmake --install "$path/../build" --config Release
fi
if command -v update-desktop-database &>/dev/null; then
echo "Updating desktop database..."
update-desktop-database "$prefix"/share/applications/ || true
fi fi
+24 -74
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:34:52 * @Date: 2025-08-05 01:34:52
* @LastEditTime: 2026-01-15 00:48:36 * @LastEditTime: 2026-01-15 03:54:42
* @Description: Configuration manager. * @Description: Configuration manager.
*/ */
#include "config.h" #include "config.h"
@@ -15,19 +15,27 @@
#include <QStandardPaths> #include <QStandardPaths>
#include "logger.h" #include "logger.h"
#include "utils.h"
using namespace GeneralLogger; using namespace GeneralLogger;
static QString expandPath(const QString& path);
const QString Config::s_DefaultConfigFileName = "config.json"; const QString Config::s_DefaultConfigFileName = "config.json";
Config::Config(const QString& configDir, const QStringList& searchDirs, QObject* parent) Config::Config(
const QString& configDir,
const QStringList& searchDirs,
const QString& configPath,
QObject* parent)
: QObject(parent), m_configDir(configDir) { : QObject(parent), m_configDir(configDir) {
debug(QString("Loading configuration from: %1 ...").arg(configDir)); if (configPath.isEmpty()) {
info(QString("Configuration directory: %1").arg(configDir));
_loadConfig(configDir + QDir::separator() + s_DefaultConfigFileName); _loadConfig(configDir + QDir::separator() + s_DefaultConfigFileName);
} else {
debug(QString("Additional search directories: %1").arg(searchDirs.join(", "))); _loadConfig(configPath);
}
if (!searchDirs.isEmpty()) {
info(QString("Additional search directories: %1").arg(searchDirs.join(", ")));
m_wallpaperConfig.dirs.append(searchDirs); m_wallpaperConfig.dirs.append(searchDirs);
}
debug("Loading wallpapers ..."); debug("Loading wallpapers ...");
_loadWallpapers(); _loadWallpapers();
@@ -37,6 +45,7 @@ Config::~Config() {
} }
void Config::_loadConfig(const QString& configPath) { void Config::_loadConfig(const QString& configPath) {
info(QString("Loading configuration from: %1").arg(configPath));
QFile configFile(configPath); QFile configFile(configPath);
if (!configFile.open(QIODevice::ReadOnly)) { if (!configFile.open(QIODevice::ReadOnly)) {
critical(QString("Failed to open config file: %1").arg(configPath)); critical(QString("Failed to open config file: %1").arg(configPath));
@@ -158,17 +167,16 @@ void Config::_loadConfig(const QString& configPath) {
if (currentObj.contains(pathParts[i]) && currentObj[pathParts[i]].isObject()) { if (currentObj.contains(pathParts[i]) && currentObj[pathParts[i]].isObject()) {
currentObj = currentObj[pathParts[i]].toObject(); currentObj = currentObj[pathParts[i]].toObject();
} else { } else {
warn(QString("Path '%1' not found").arg(pathParts.mid(0, i + 1).join('.'))); debug(QString("Path '%1' not found").arg(pathParts.mid(0, i + 1).join('.')));
return; return;
} }
} }
// 获取目标值
const QString& finalKey = pathParts.last(); const QString& finalKey = pathParts.last();
if (currentObj.contains(finalKey)) { if (currentObj.contains(finalKey)) {
mapping.parser(currentObj[finalKey]); mapping.parser(currentObj[finalKey]);
} else { } else {
warn(QString("Key '%1' not found in '%2'").arg(finalKey).arg(mapping.path)); debug(QString("Key '%1' not found in '%2'").arg(finalKey).arg(mapping.path));
} }
})(); })();
} }
@@ -187,11 +195,11 @@ void Config::_loadWallpapers() {
debug(QString("Loading wallpapers from %1 specified directories...").arg(m_wallpaperConfig.dirs.size())); debug(QString("Loading wallpapers from %1 specified directories...").arg(m_wallpaperConfig.dirs.size()));
for (const QString& dirPath : m_wallpaperConfig.dirs) { for (const QString& dirPath : m_wallpaperConfig.dirs) {
QDir dir(dirPath); QDir dir(dirPath);
if (dir.exists()) { if (checkDir(dirPath)) {
QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
for (const QString& file : files) { for (const QString& file : files) {
QString filePath = dir.filePath(file); QString filePath = dir.filePath(file);
paths.insert(filePath); paths.insert(expandPath(filePath));
} }
} else { } else {
warn(QString("Directory '%1' does not exist").arg(dirPath)); warn(QString("Directory '%1' does not exist").arg(dirPath));
@@ -205,70 +213,12 @@ void Config::_loadWallpapers() {
m_wallpapers.reserve(paths.size()); m_wallpapers.reserve(paths.size());
for (const QString& path : paths) { for (const QString& path : paths) {
if (isValidImageFile(path)) { if (checkImageFile(path)) {
m_wallpapers.append(path); m_wallpapers.append(path);
} else {
warn(QString("File '%1' is not recognized as a valid image file").arg(path));
} }
} }
info(QString("Found %1 wallpapers").arg(paths.size())); info(QString("Found %1 files").arg(paths.size()));
}
bool Config::isValidImageFile(const QString& filePath) {
static const QStringList validExtensions = {
".jpg",
".jpeg",
".jfif",
".png",
".bmp",
".gif",
".webp",
".tiff",
".avif",
".heic",
".heif"};
// check if exist
if (!QFile::exists(filePath)) {
warn(QString("File does not exist: %1").arg(filePath));
return false;
}
// check if normal file
QFileInfo fileInfo(filePath);
if (!(fileInfo.isFile() || fileInfo.isSymbolicLink()) || !fileInfo.isReadable()) {
warn(QString("Invalid file: %1").arg(filePath));
return false;
}
// check if valid extension
for (const QString& ext : validExtensions) {
if (filePath.endsWith(ext, Qt::CaseInsensitive)) {
return true;
}
}
warn(QString("Unsupported file type: %1").arg(filePath));
return false;
}
static QString expandPath(const QString& path) {
QString expandedPath = path;
if (expandedPath.startsWith("~/")) {
expandedPath.replace(0, 1, QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
} else if (expandedPath == "~") {
expandedPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
}
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QRegularExpression envVarRegex(R"(\$([A-Za-z_][A-Za-z0-9_]*))");
QRegularExpressionMatchIterator i = envVarRegex.globalMatch(expandedPath);
while (i.hasNext()) {
QRegularExpressionMatch match = i.next();
QString varName = match.captured(1);
QString varValue = env.value(varName);
if (!varValue.isEmpty()) {
expandedPath.replace(match.captured(0), varValue);
}
}
return QDir::cleanPath(expandedPath);
} }
+6 -4
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:34:52 * @Date: 2025-08-05 01:34:52
* @LastEditTime: 2025-08-07 23:24:02 * @LastEditTime: 2026-01-15 03:40:25
* @Description: Configuration manager. * @Description: Configuration manager.
*/ */
#ifndef CONFIG_H #ifndef CONFIG_H
@@ -46,12 +46,14 @@ class Config : public QObject {
bool reverse = false; bool reverse = false;
}; };
Config(const QString& configDir, const QStringList& searchDirs = {}, QObject* parent = nullptr); Config(
const QString& configDir,
const QStringList& searchDirs = {},
const QString& configPath = "",
QObject* parent = nullptr);
~Config(); ~Config();
static bool isValidImageFile(const QString& filePath);
[[nodiscard]] const QStringList& getWallpapers() const { return m_wallpapers; } [[nodiscard]] const QStringList& getWallpapers() const { return m_wallpapers; }
[[nodiscard]] qint64 getWallpaperCount() const { return m_wallpapers.size(); } [[nodiscard]] qint64 getWallpaperCount() const { return m_wallpapers.size(); }
+1 -4
View File
@@ -1,15 +1,12 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:22:53 * @Date: 2025-08-05 01:22:53
* @LastEditTime: 2026-01-14 23:26:32 * @LastEditTime: 2026-01-15 03:42:17
* @Description: Animated carousel widget for displaying and selecting images. * @Description: Animated carousel widget for displaying and selecting images.
*/ */
#ifndef IMAGES_CAROUSEL_H #ifndef IMAGES_CAROUSEL_H
#define IMAGES_CAROUSEL_H #define IMAGES_CAROUSEL_H
#include <qqueue.h>
#include <qtmetamacros.h>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QKeyEvent> #include <QKeyEvent>
#include <QMutex> #include <QMutex>
+29 -28
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-07 01:12:37 * @Date: 2025-08-07 01:12:37
* @LastEditTime: 2026-01-15 02:10:15 * @LastEditTime: 2026-01-15 02:34:40
* @Description: Implementation of logger. * @Description: Implementation of logger.
*/ */
#include "logger.h" #include "logger.h"
@@ -15,12 +15,13 @@
#include <QTextStream> #include <QTextStream>
#include <cstdio> #include <cstdio>
Q_LOGGING_CATEGORY(logMain, "wallpaper.carousel") #include "version.h"
static QTextStream* g_logStream = nullptr; Q_LOGGING_CATEGORY(logMain, APP_NAME)
static bool g_isColored = false;
static QMutex g_logMutex; static QTextStream* s_logStream = nullptr;
static const QString g_appName = "wallpaper.carousel"; // same as above static bool s_isColored = false;
static QMutex s_logMutex;
static bool checkIsColored(FILE* stream) { static bool checkIsColored(FILE* stream) {
if (!stream || !isatty(fileno(stream))) { if (!stream || !isatty(fileno(stream))) {
@@ -37,51 +38,51 @@ static bool checkIsColored(FILE* stream) {
static void messageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { static void messageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) {
Q_UNUSED(context); Q_UNUSED(context);
QMutexLocker locker(&g_logMutex); QMutexLocker locker(&s_logMutex);
QString levelTag; QString levelTag;
QString colorTag; QString colorTag;
QString colorText; QString colorText;
QString resetColor = g_isColored ? "\033[0m" : ""; QString resetColor = s_isColored ? "\033[0m" : "";
switch (type) { switch (type) {
case QtDebugMsg: case QtDebugMsg:
levelTag = "[DEBUG]"; levelTag = "[DEBUG]";
colorTag = g_isColored ? "\033[36m" : ""; // Cyan colorTag = s_isColored ? "\033[36m" : ""; // Cyan
break; break;
case QtInfoMsg: case QtInfoMsg:
levelTag = "[INFO]"; levelTag = "[INFO]";
colorTag = g_isColored ? "\033[92m" : ""; // Light Green colorTag = s_isColored ? "\033[92m" : ""; // Light Green
colorText = g_isColored ? "\033[32m" : ""; // Green colorText = s_isColored ? "\033[32m" : ""; // Green
break; break;
case QtWarningMsg: case QtWarningMsg:
levelTag = "[WARN]"; levelTag = "[WARN]";
colorTag = g_isColored ? "\033[93m" : ""; // Light Yellow colorTag = s_isColored ? "\033[93m" : ""; // Light Yellow
colorText = g_isColored ? "\033[33m" : ""; // Yellow colorText = s_isColored ? "\033[33m" : ""; // Yellow
break; break;
case QtCriticalMsg: case QtCriticalMsg:
levelTag = "[ERROR]"; levelTag = "[ERROR]";
colorTag = g_isColored ? "\033[91m" : ""; // Light Red colorTag = s_isColored ? "\033[91m" : ""; // Light Red
colorText = g_isColored ? "\033[31m" : ""; // Red colorText = s_isColored ? "\033[31m" : ""; // Red
break; break;
case QtFatalMsg: case QtFatalMsg:
levelTag = "[FATAL]"; levelTag = "[FATAL]";
colorTag = g_isColored ? "\033[95m" : ""; // Magenta colorTag = s_isColored ? "\033[95m" : ""; // Magenta
break; break;
} }
if (g_logStream) { if (s_logStream) {
(*g_logStream) << colorTag << levelTag << " " << colorText << msg << resetColor << Qt::endl; (*s_logStream) << colorTag << levelTag << " " << colorText << msg << resetColor << Qt::endl;
g_logStream->flush(); s_logStream->flush();
} }
} }
void Logger::init(FILE* stream) { void Logger::init(FILE* stream) {
if (stream) { if (stream) {
delete g_logStream; delete s_logStream;
g_logStream = new QTextStream(stream); s_logStream = new QTextStream(stream);
} }
g_isColored = checkIsColored(stream); s_isColored = checkIsColored(stream);
qInstallMessageHandler(messageOutput); qInstallMessageHandler(messageOutput);
} }
@@ -89,25 +90,25 @@ void Logger::init(FILE* stream) {
void Logger::setLogLevel(QtMsgType level) { void Logger::setLogLevel(QtMsgType level) {
switch (level) { switch (level) {
case QtDebugMsg: case QtDebugMsg:
QLoggingCategory::setFilterRules(QString("%1.debug=true").arg(g_appName)); QLoggingCategory::setFilterRules(QString("%1.debug=true").arg(APP_NAME));
break; break;
case QtInfoMsg: case QtInfoMsg:
QLoggingCategory::setFilterRules(QString("%1.debug=false\n%1.info=true").arg(g_appName)); QLoggingCategory::setFilterRules(QString("%1.debug=false\n%1.info=true").arg(APP_NAME));
break; break;
case QtWarningMsg: case QtWarningMsg:
QLoggingCategory::setFilterRules(QString("%1.debug=false\n%1.info=false\n%1.warning=true").arg(g_appName)); QLoggingCategory::setFilterRules(QString("%1.debug=false\n%1.info=false\n%1.warning=true").arg(APP_NAME));
break; break;
case QtCriticalMsg: case QtCriticalMsg:
QLoggingCategory::setFilterRules(QString("%1.debug=false\n%1.info=false\n%1.warning=false\n%1.critical=true").arg(g_appName)); QLoggingCategory::setFilterRules(QString("%1.debug=false\n%1.info=false\n%1.warning=false\n%1.critical=true").arg(APP_NAME));
break; break;
case QtFatalMsg: case QtFatalMsg:
QLoggingCategory::setFilterRules(QString("%1.debug=false\n%1.info=false\n%1.warning=false\n%1.critical=false").arg(g_appName)); QLoggingCategory::setFilterRules(QString("%1.debug=false\n%1.info=false\n%1.warning=false\n%1.critical=false").arg(APP_NAME));
break; break;
} }
} }
void Logger::quiet() { void Logger::quiet() {
QLoggingCategory::setFilterRules(QString("%1.debug=false\n%1.info=false\n%1.warning=false\n%1.critical=false\n%1.fatal=false").arg(g_appName)); QLoggingCategory::setFilterRules(QString("%1.debug=false\n%1.info=false\n%1.warning=false\n%1.critical=false\n%1.fatal=false").arg(APP_NAME));
} }
void GeneralLogger::debug(const QString& msg) { void GeneralLogger::debug(const QString& msg) {
+111 -35
View File
@@ -1,13 +1,11 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 00:37:58 * @Date: 2025-08-05 00:37:58
* @LastEditTime: 2026-01-15 01:43:14 * @LastEditTime: 2026-01-15 03:41:55
* @Description: Entry point. * @Description: Argument parser and entry point.
*/ */
#include <qapplication.h>
#include <qlogging.h>
#include <QApplication> #include <QApplication>
#include <QCommandLineParser>
#include <QDir> #include <QDir>
#include <QStandardPaths> #include <QStandardPaths>
#include <QTextStream> #include <QTextStream>
@@ -15,49 +13,127 @@
#include "config.h" #include "config.h"
#include "logger.h" #include "logger.h"
#include "main_window.h" #include "main_window.h"
#include "utils.h"
#include "version.h"
static class AppOptions {
QCommandLineParser parser{};
void printVersion() {
QTextStream out(stdout);
out << APP_NAME << " version " << APP_VERSION << Qt::endl;
doReturn = true;
}
void printHelp() {
QTextStream out(stdout);
out << parser.helpText() << Qt::endl;
doReturn = true;
}
void printError() {
if (!errorText.isEmpty()) {
QTextStream out(stderr);
out << errorText << Qt::endl;
printHelp();
}
doReturn = true;
}
public:
QString configPath = "";
QStringList appendDirs;
QString errorText = "";
bool doReturn = false;
void parseArgs(QApplication* a) {
parser.setApplicationDescription("A small wallpaper utility made with Qt");
const QCommandLineOption helpOption = parser.addHelpOption();
const QCommandLineOption versionOption = parser.addVersionOption();
QCommandLineOption verboseOption(QStringList() << "V" << "verbose", "Set log level to DEBUG");
parser.addOption(verboseOption);
QCommandLineOption quietOption(QStringList() << "q" << "quiet", "Suppress all log output");
parser.addOption(quietOption);
QCommandLineOption appendDirOption(QStringList() << "d" << "append-dir", "Append an additional wallpaper search directory", "dir");
parser.addOption(appendDirOption);
QCommandLineOption configFileOption(QStringList() << "c" << "config-file", "Specify a custom configuration file", "file");
parser.addOption(configFileOption);
if (!parser.parse(a->arguments())) {
errorText = parser.errorText();
doReturn = true;
return;
}
if (parser.isSet(versionOption)) {
printVersion();
return;
}
if (parser.isSet(helpOption)) {
printHelp();
return;
}
if (parser.isSet(verboseOption)) {
Logger::setLogLevel(QtDebugMsg);
} else if (parser.isSet(quietOption)) {
Logger::quiet();
} else {
Logger::setLogLevel(QtInfoMsg);
}
for (const QString& dir : parser.values(appendDirOption)) {
if (checkDir(dir)) {
appendDirs.append(dir);
} else {
errorText = QString("Error: Directory does not exist or is not accessible: %1").arg(dir);
printError();
return;
}
}
if (parser.isSet(configFileOption)) {
QString path = parser.value(configFileOption);
if (checkFile(path)) {
configPath = path;
} else {
errorText = QString("Error: Config file does not exist or is not accessible: %1").arg(path);
printError();
return;
}
}
}
} s_options;
static QString getConfigDir() { static QString getConfigDir() {
auto configDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); auto configDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
if (configDir.isEmpty()) { if (configDir.isEmpty()) {
configDir = QDir::homePath() + QDir::separator() + ".config" + QDir::separator() + "wallpaper_chooser"; configDir = QDir::homePath() + QDir::separator() + ".config" + QDir::separator() + APP_NAME;
} }
QDir().mkpath(configDir); QDir().mkpath(configDir);
return configDir; return configDir;
} }
static void setLogLevel(int argc, char* argv[]) {
for (int i = 1; i < argc; ++i) {
QString arg(argv[i]);
if (arg == "--debug" || arg == "-vvv" || arg == "--verbose") {
Logger::setLogLevel(QtDebugMsg);
return;
} else if (arg == "--info" || arg == "-vv") {
Logger::setLogLevel(QtInfoMsg);
return;
} else if (arg == "--warn" || arg == "-v") {
Logger::setLogLevel(QtWarningMsg);
return;
} else if (arg == "--critical") {
Logger::setLogLevel(QtCriticalMsg);
return;
} else if (arg == "--fatal") {
Logger::setLogLevel(QtFatalMsg);
return;
} else if (arg == "--quiet") {
Logger::quiet();
return;
}
}
Logger::setLogLevel(QtInfoMsg);
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
QApplication a(argc, argv); QApplication a(argc, argv);
a.setApplicationName(APP_NAME);
a.setApplicationVersion(APP_VERSION);
Logger::init(stderr); Logger::init();
setLogLevel(argc, argv); s_options.parseArgs(&a);
Config config(getConfigDir()); if (s_options.doReturn) {
return s_options.errorText.isEmpty() ? 0 : 1;
}
Config config(getConfigDir(), s_options.appendDirs, s_options.configPath, &a);
MainWindow w(config); MainWindow w(config);
w.show(); w.show();
+73 -1
View File
@@ -1,13 +1,18 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-11-30 20:59:57 * @Date: 2025-11-30 20:59:57
* @LastEditTime: 2025-12-07 06:08:18 * @LastEditTime: 2026-01-15 03:11:08
* @Description: THE utils header that every project needs :) * @Description: THE utils header that every project needs :)
*/ */
#ifndef UTILS_H #ifndef UTILS_H
#define UTILS_H #define UTILS_H
#include <QDir>
#include <QFileInfo>
#include <QProcess>
#include <QRegularExpression>
#include <QStandardPaths>
#include <utility> #include <utility>
template <typename Callable> template <typename Callable>
@@ -26,4 +31,71 @@ class Defer {
} }
}; };
inline bool checkFile(const QString& path) {
QFileInfo checkFile(path);
return checkFile.exists() && checkFile.isFile() && checkFile.isReadable();
}
inline bool checkDir(const QString& path) {
QFileInfo checkFile(path);
return checkFile.exists() && checkFile.isDir() && checkFile.isReadable();
}
inline QString expandPath(const QString& path) {
QString expandedPath = path;
if (expandedPath.startsWith("~/")) {
expandedPath.replace(0, 1, QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
} else if (expandedPath == "~") {
expandedPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
}
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QRegularExpression envVarRegex(R"(\$([A-Za-z_][A-Za-z0-9_]*))");
QRegularExpressionMatchIterator i = envVarRegex.globalMatch(expandedPath);
while (i.hasNext()) {
QRegularExpressionMatch match = i.next();
QString varName = match.captured(1);
QString varValue = env.value(varName);
if (!varValue.isEmpty()) {
expandedPath.replace(match.captured(0), varValue);
}
}
return QDir::cleanPath(expandedPath);
}
inline bool checkImageFile(const QString& filePath) {
static const QStringList validExtensions = {
".jpg",
".jpeg",
".jfif",
".png",
".bmp",
".gif",
".webp",
".tiff",
".avif",
".heic",
".heif"};
// check if exist
if (!QFile::exists(filePath)) {
return false;
}
// check if normal file
QFileInfo fileInfo(filePath);
if (!(fileInfo.isFile() || fileInfo.isSymbolicLink()) || !fileInfo.isReadable()) {
return false;
}
// check if valid extension
for (const QString& ext : validExtensions) {
if (filePath.endsWith(ext, Qt::CaseInsensitive)) {
return true;
}
}
return false;
}
#endif // UTILS_H #endif // UTILS_H
+2
View File
@@ -0,0 +1,2 @@
#define APP_VERSION "@PROJECT_VERSION@"
#define APP_NAME "@EXECUTABLE_NAME@"