diff --git a/CMakeLists.txt b/CMakeLists.txt index a743ad4fee..56d3fa6b34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,14 @@ message(STATUS "SCOPY_RESOURCE_FILES: " ${SCOPY_RESOURCE_FILES}) option(ENABLE_TRANSLATION "Enable translation" ON) include(ScopyTranslation) + +find_package(Python3 COMPONENTS Interpreter) +message("------------------------------${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/pyScripts/main.py ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}") +execute_process(COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/pyScripts/main.py ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) +execute_process(COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/pyScripts/json.py ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + + + if(ENABLE_TRANSLATION) generate_translations() qt_add_resources(SCOPY_RESOURCES ${CMAKE_BINARY_DIR}/translations.qrc) diff --git a/gui/src/plotwidget.cpp b/gui/src/plotwidget.cpp index 06b66f53c1..fe2ebeeb25 100644 --- a/gui/src/plotwidget.cpp +++ b/gui/src/plotwidget.cpp @@ -2,6 +2,7 @@ #include "DisplayPlot.h" #include "plotaxis.h" +#include "style_variables.h" #include #include @@ -20,6 +21,7 @@ #include #include #include +#include using namespace scopy; @@ -56,7 +58,8 @@ PlotWidget::PlotWidget(QWidget *parent) setAxisScalesVisible(true); // Plot needs a grid - QPen gridpen(QColor("#353537")); + qss::button::test; + QPen gridpen((QColor(theme::dark::highlight))); EdgelessPlotGrid *d_grid = new EdgelessPlotGrid(); QColor majorPenColor(gridpen.color()); d_grid->setMajorPen(majorPenColor, 1.0, Qt::DashLine); diff --git a/plugins/testplugin/include/testplugin/scopy-testplugin_export.h b/plugins/testplugin/include/testplugin/scopy-testplugin_export.h new file mode 100644 index 0000000000..cc793e99b3 --- /dev/null +++ b/plugins/testplugin/include/testplugin/scopy-testplugin_export.h @@ -0,0 +1,42 @@ + +#ifndef SCOPY_TESTPLUGIN_EXPORT_H +#define SCOPY_TESTPLUGIN_EXPORT_H + +#ifdef SCOPY_TESTPLUGIN_STATIC_DEFINE +# define SCOPY_TESTPLUGIN_EXPORT +# define SCOPY_TESTPLUGIN_NO_EXPORT +#else +# ifndef SCOPY_TESTPLUGIN_EXPORT +# ifdef scopy_testplugin_EXPORTS + /* We are building this library */ +# define SCOPY_TESTPLUGIN_EXPORT __attribute__((visibility("default"))) +# else + /* We are using this library */ +# define SCOPY_TESTPLUGIN_EXPORT __attribute__((visibility("default"))) +# endif +# endif + +# ifndef SCOPY_TESTPLUGIN_NO_EXPORT +# define SCOPY_TESTPLUGIN_NO_EXPORT __attribute__((visibility("hidden"))) +# endif +#endif + +#ifndef SCOPY_TESTPLUGIN_DEPRECATED +# define SCOPY_TESTPLUGIN_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef SCOPY_TESTPLUGIN_DEPRECATED_EXPORT +# define SCOPY_TESTPLUGIN_DEPRECATED_EXPORT SCOPY_TESTPLUGIN_EXPORT SCOPY_TESTPLUGIN_DEPRECATED +#endif + +#ifndef SCOPY_TESTPLUGIN_DEPRECATED_NO_EXPORT +# define SCOPY_TESTPLUGIN_DEPRECATED_NO_EXPORT SCOPY_TESTPLUGIN_NO_EXPORT SCOPY_TESTPLUGIN_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef SCOPY_TESTPLUGIN_NO_DEPRECATED +# define SCOPY_TESTPLUGIN_NO_DEPRECATED +# endif +#endif + +#endif /* SCOPY_TESTPLUGIN_EXPORT_H */ diff --git a/pyScripts/json.py b/pyScripts/json.py new file mode 100644 index 0000000000..1ca55aff5d --- /dev/null +++ b/pyScripts/json.py @@ -0,0 +1,56 @@ +import os +import main + + +def parse_json_file(filepath): + with open(filepath, 'r') as file: + content = file.read() + + content = content.strip() + if content[0] != '{' or content[-1] != '}': + raise ValueError(f"Invalid JSON format in file {filepath}") + + json_content = {} + content = content[1:-1].strip() # Remove the outer braces + items = content.split(',') + + for item in items: + key, value = item.split(':') + key = key.strip().strip('"') + value = value.strip().strip('"') + json_content[key] = os.path.relpath(filepath, style_folder).replace("/", "_").replace(file_extension, "") + "_" + key + + return json_content + + +def generate_variable(filepath, indent): + with open(filepath, 'r') as file: + if len(file.read()) == 0: + return '' + + json = parse_json_file(filepath) + + variable = "" + for key in json: + variable += indent + 'const char* ' + key + ' = "' + json[key] + '";\n' + + return variable[:-1] + + +if __name__ == "__main__": + import sys + + if len(sys.argv) != 3: + print("Usage: python generate_json_header.py ") + sys.exit(1) + source_folder = sys.argv[1] + build_folder = sys.argv[2] + + header_name = "style_variables.h" + header_path = source_folder + "/gui/include/gui/style_variables.h" + file_extension = '.json' + style_folder = source_folder + "/style" + + namespace_structure = main.create_namespace_structure(style_folder, file_extension) + namespace_code = main.generate_namespace_code(namespace_structure, generate_variable) + main.write_header_file(header_path, namespace_code, header_name) diff --git a/pyScripts/main.py b/pyScripts/main.py new file mode 100644 index 0000000000..2ff9d11df5 --- /dev/null +++ b/pyScripts/main.py @@ -0,0 +1,97 @@ +import os +import sys + + +def create_namespace_structure(folder, file_extension): + namespace_structure = {} + + for dirpath, dirnames, filenames in os.walk(folder): + qss_files = [f for f in filenames if f.endswith(file_extension)] + if qss_files: + relative_path = os.path.relpath(dirpath, folder) + namespace_structure[relative_path] = { + os.path.splitext(f)[0]: os.path.join(dirpath, f).replace('\\', '/') + for f in qss_files + } + + return namespace_structure + + +def replace_property(filepath, value): + search_text = "&&property&&" + with open(filepath, 'r') as file: + data = file.read() + data = data.replace(search_text, value) + + with open(qss_path, 'a') as file: + file.write(data) + + +def generate_variable(filepath, indent): + value = os.path.relpath(filepath, qss_folder).replace("/", "_").replace(file_extension, "") + replace_property(filepath, value) + + return f'{indent}const char* ' + os.path.basename(filepath).replace(file_extension, '') + f' = "{value}";' + + +def generate_namespace_code(namespace_structure, variable_func): + code_lines = [] + + def add_namespaces(path_parts, files, level): + indent = '\t' * level + code_lines.append(f'{indent}namespace {path_parts[level]} {{') + if level + 1 == len(path_parts): + for filename, filepath in files.items(): + code_lines.append(variable_func(filepath, f'{indent}\t')) + else: + subnamespace = {k: v for k, v in namespace_structure.items() if + k.startswith('/'.join(path_parts[:level + 1]) + '/')} + if subnamespace: + for subpath, subfiles in subnamespace.items(): + subpath_parts = subpath.split(os.sep) + add_namespaces(subpath_parts, subfiles, level + 1) + code_lines.append(f'{indent}}} // namespace {path_parts[level]}') + + root_parts = sorted(namespace_structure.keys(), key=lambda x: x.count(os.sep)) + added_namespaces = set() + + for root_path in root_parts: + path_parts = root_path.split(os.sep) + if path_parts[0] not in added_namespaces: + add_namespaces(path_parts, namespace_structure[root_path], 0) + added_namespaces.add(path_parts[0]) + + return '\n'.join(code_lines) + + +def write_header_file(output_file, namespace_code, name): + def_name = name.upper().replace(".", "_") + + with open(output_file, 'w') as f: + f.write("#ifndef " + def_name + "\n") + f.write("#define " + def_name + "\n") + f.write("#include \n\n") + f.write(namespace_code) + f.write("\n\n#endif // " + def_name + "\n") + + +# https://stackoverflow.com/questions/49018868/how-do-i-make-cmake-run-a-python-script-before-building-in-order-to-generate-fi + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Error") + sys.exit(1) + + source_folder = sys.argv[1] + build_folder = sys.argv[2] + + header_name = "style.h" + header_path = source_folder + "/gui/include/gui/" + header_name + qss_path = build_folder + "/style.qss" + qss_folder = source_folder + "/style" + file_extension = '.qss' + + open(qss_path, 'w') + namespace_code = generate_namespace_code(create_namespace_structure(qss_folder, file_extension), generate_variable) + write_header_file(header_path, namespace_code, header_name) diff --git a/style/json/globals/dimensions.json b/style/json/globals/dimensions.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/style/json/globals/scripts.json b/style/json/globals/scripts.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/style/qss/button/square.qss b/style/qss/button/square.qss new file mode 100644 index 0000000000..cafbb079ab --- /dev/null +++ b/style/qss/button/square.qss @@ -0,0 +1,14 @@ +QLabel[&&property&&="true"] { + background-color: &transparent&; // rgba(255, 255, 255, 0) + + &add_font_small& + // color: white; + // font-weight: 500; + // font-family: Open Sans; + // font-size: 12px; + // font-style: normal; +} + +QLabel[&&property&&="true"]:disabled { + color: &color_highlight_2&; // grey +} diff --git a/style/qss/button/test.qss b/style/qss/button/test.qss new file mode 100644 index 0000000000..ad94c03976 --- /dev/null +++ b/style/qss/button/test.qss @@ -0,0 +1,3 @@ +QPushButton[&&property&&=true]{ + size: 2px +} diff --git a/style/qss/combobox/idk/combo.qss b/style/qss/combobox/idk/combo.qss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/style/qss/label/big.qss b/style/qss/label/big.qss new file mode 100644 index 0000000000..bbe0a01e34 --- /dev/null +++ b/style/qss/label/big.qss @@ -0,0 +1,26 @@ +QPushButton[&&property&&="true"] { + width: &unit_pixel_1&; // 48px + height: &unit_pixel_1&; // 48px + + &add_border_small& + // border-radius: 0px 2px 2px 0px; + // border-style: outset; + + qproperty-iconSize: &1pixel_unit&; // 48px + background-color: &color_highlight_1&; // #272730 + + &add_font_small& + // color: white; + // font-weight: 700; + // font-size: 14px; + + height: &unit_pixel_1&; +} + +QPushButton[&&property&&="true"]:checked { + background-color: &color_highlight_2&; // #4A64FF +} + +QPushButton[&&property&&="true"]:pressed { + background-color: &color_highlight_2&; // #4A64FF +} diff --git a/style/qss/label/medium.qss b/style/qss/label/medium.qss new file mode 100644 index 0000000000..bbe0a01e34 --- /dev/null +++ b/style/qss/label/medium.qss @@ -0,0 +1,26 @@ +QPushButton[&&property&&="true"] { + width: &unit_pixel_1&; // 48px + height: &unit_pixel_1&; // 48px + + &add_border_small& + // border-radius: 0px 2px 2px 0px; + // border-style: outset; + + qproperty-iconSize: &1pixel_unit&; // 48px + background-color: &color_highlight_1&; // #272730 + + &add_font_small& + // color: white; + // font-weight: 700; + // font-size: 14px; + + height: &unit_pixel_1&; +} + +QPushButton[&&property&&="true"]:checked { + background-color: &color_highlight_2&; // #4A64FF +} + +QPushButton[&&property&&="true"]:pressed { + background-color: &color_highlight_2&; // #4A64FF +} diff --git a/style/qss/label/small.qss b/style/qss/label/small.qss new file mode 100644 index 0000000000..bbe0a01e34 --- /dev/null +++ b/style/qss/label/small.qss @@ -0,0 +1,26 @@ +QPushButton[&&property&&="true"] { + width: &unit_pixel_1&; // 48px + height: &unit_pixel_1&; // 48px + + &add_border_small& + // border-radius: 0px 2px 2px 0px; + // border-style: outset; + + qproperty-iconSize: &1pixel_unit&; // 48px + background-color: &color_highlight_1&; // #272730 + + &add_font_small& + // color: white; + // font-weight: 700; + // font-size: 14px; + + height: &unit_pixel_1&; +} + +QPushButton[&&property&&="true"]:checked { + background-color: &color_highlight_2&; // #4A64FF +} + +QPushButton[&&property&&="true"]:pressed { + background-color: &color_highlight_2&; // #4A64FF +} diff --git a/style/theme/dark/colors.json b/style/theme/dark/colors.json new file mode 100644 index 0000000000..31997299ad --- /dev/null +++ b/style/theme/dark/colors.json @@ -0,0 +1,4 @@ +{ +"background": "#121212", +"highlight" : "#890000" +} diff --git a/style/theme/light/colors.json b/style/theme/light/colors.json new file mode 100644 index 0000000000..e69de29bb2