Skip to content

Commit

Permalink
style structure
Browse files Browse the repository at this point in the history
  • Loading branch information
andrei47w committed Jun 13, 2024
1 parent efe2fbe commit 4d28892
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,5 @@ iio-widgets/include/iio-widgets/scopy-iio-widgets_export.h
iioutil/include/iioutil/scopy-iioutil_export.h
pluginbase/include/pluginbase/scopy-pluginbase_config.h
pluginbase/include/pluginbase/scopy-pluginbase_export.h
gui/include/gui/style_properties.h
gui/include/gui/style_attributes.h
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
execute_process(COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/style_generator.py ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE ret)
if(NOT ret EQUAL "0")
message( FATAL_ERROR "Failed to generate style files! error: ${ret}")
else()
message("-- Generated style files")
endif()

if(ENABLE_TRANSLATION)
generate_translations()
qt_add_resources(SCOPY_RESOURCES ${CMAKE_BINARY_DIR}/translations.qrc)
Expand Down
32 changes: 32 additions & 0 deletions core/include/core/style.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef STYLEREPOSITORY_H
#define STYLEREPOSITORY_H

#include <QObject>
#include "scopy-core_export.h"

namespace scopy {
class SCOPY_CORE_EXPORT Style : public QObject
{
Q_OBJECT
public:
Style(QObject *parent = nullptr);
~Style();

void init();

static QString getAttribute(char *key);
static QColor getColor(char *key);
static int getDimension(char *key);
static void setStyle(QWidget *widget, char *style);

protected:
void applyStyle();

private:
static QJsonDocument *m_json;
QString m_jsonPath;
QString m_qssPath;
};
} // namespace scopy

#endif // STYLEREPOSITORY_H
54 changes: 54 additions & 0 deletions core/src/style.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "style.h"
#include "qcolor.h"
#include "qjsonobject.h"
#include "qwidget.h"

#include <QApplication>
#include <QFile>
#include <QJsonDocument>

using namespace scopy;

QJsonDocument *Style::m_json{new QJsonDocument()};

Style::Style(QObject *parent)
: QObject(parent)
, m_jsonPath("style_variables.json")
, m_qssPath("style.qss")
{}

Style::~Style() {}

void Style::init()
{
QFile file(m_jsonPath);
file.open(QIODevice::ReadOnly);
QByteArray data = file.readAll();
file.close();
m_json = new QJsonDocument(QJsonDocument::fromJson(data));

applyStyle();
}

void Style::setStyle(QWidget *widget, char *style) { widget->setProperty(style, true); }

QString Style::getAttribute(char *key) { return m_json->object().value(key).toString(); }

QColor Style::getColor(char *key) { return QColor(getAttribute(key)); }

int Style::getDimension(char *key) { return getAttribute(key).toInt(); }

void Style::applyStyle()
{
QFile file(m_qssPath);
file.open(QIODevice::ReadOnly);
QString style = QString(file.readAll());
file.close();

for(const QString &key : m_json->object().keys()) {
QJsonValue value = m_json->object().value(key);
style.replace("&" + key + "&", value.toString());
}

qApp->setStyleSheet(style);
}
5 changes: 3 additions & 2 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <core/scopymainwindow_api.h>
#include <gui/utils.h>
#include <core/crashreport.h>
#include <core/style.h>

using namespace scopy;

Expand Down Expand Up @@ -101,8 +102,8 @@ int main(int argc, char *argv[])
printRuntimeEnvironmentInfo();
ApplicationRestarter restarter(QString::fromLocal8Bit(argv[0]));
a.setWindowIcon(QIcon(":/gui/icon.ico"));
a.setStyle("Fusion");
a.setStyleSheet(Util::loadStylesheetFromFile(":/gui/stylesheets/default.qss"));
Style().init();

ScopyMainWindow w;
w.show();

Expand Down
179 changes: 179 additions & 0 deletions tools/style_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import os
import sys


def create_namespace_structure(extension):
namespace_structure = {}

for dirpath, dirnames, filenames in os.walk(style_folder):
qss_files = [f for f in filenames if f.endswith(extension)]
if qss_files:
relative_path = os.path.relpath(dirpath, style_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()
if search_text not in data:
return False
data = data.replace(search_text, value)

with open(qss_path, 'a') as file:
file.write(data)

return True


def generate_qss_variable(filepath, indent):
value = os.path.relpath(filepath, style_folder).replace("/", "_").replace(".qss", "")
if not replace_property(filepath, value):
return ""

return f'{indent}const char *const ' + os.path.basename(filepath).replace(".qss", '') + f' = "{value}";'


def generate_namespace_code(namespace_structure, variable_func):
code_lines = []

def add_namespaces(folder, files, level):
indent = '\t' * level
code_lines.append(f'{indent}namespace {folder[level]} {{')
if level + 1 == len(folder):
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(folder[: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 {folder[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 <scopy-gui_export.h>\n\n")
f.write(namespace_code)
f.write("\n\n#endif // " + def_name + "\n")


def open_qss_file():
open(qss_path, 'w')

def append_variable(key, value):
with open(json_path, 'a') as file:
file.write("\t\"" + key + "\": " + value + ",\n")


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('"')
formatted_key = os.path.relpath(filepath, style_folder).replace("/", "_").replace(".json",
"") + "_" + key
json_content[key] = formatted_key
append_variable(formatted_key, value)

return json_content


def generate_json_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]



def open_json_file():
with open(json_path, 'w') as file:
file.write("{\n")


def close_json_file():
with open(json_path, 'r+') as file:
content = file.read()
last_comma_index = content.rfind(',')
if last_comma_index != -1:
content = content[:last_comma_index]
file.seek(0)
file.write(content)
file.truncate()
file.write("\n}\n")


if __name__ == "__main__":
if len(sys.argv) != 3:
print("Error")
sys.exit(1)

source_folder = sys.argv[1]
build_folder = sys.argv[2]
style_folder = source_folder + "/gui/style"

# qss
qss_header_name = "style_properties.h"
qss_header_path = source_folder + "/gui/include/gui/" + qss_header_name
qss_name = "style.qss"
qss_path = build_folder + "/" + qss_name

open_qss_file()
qss_namespace_code = generate_namespace_code(create_namespace_structure("qss"),
generate_qss_variable)
write_header_file(qss_header_path, qss_namespace_code, qss_header_name)

# json
json_header_name = "style_attributes.h"
json_header_path = source_folder + "/gui/include/gui/" + json_header_name
json_name = "style_attributes.json"
json_path = build_folder + "/" + json_name

open_json_file()
json_namespace_code = generate_namespace_code(create_namespace_structure(".json"),
generate_json_variable)
write_header_file(json_header_path, json_namespace_code, json_header_name)
close_json_file()

sys.exit(0)

0 comments on commit 4d28892

Please sign in to comment.