48 Commits

Author SHA1 Message Date
1bb800f1e7 experimental painting 2026-01-24 17:50:23 +01:00
Christoph Holzheuer
7647b01d62 Language updates. 2026-01-23 16:33:05 +01:00
37fcc5e888 Created new painter bug. 2026-01-22 22:16:19 +01:00
Christoph Holzheuer
ab4abd214e Fixed value display bug. 2026-01-21 17:07:00 +01:00
07c235afa2 Fixed style 2026-01-20 23:02:00 +01:00
Christoph Holzheuer
4f44c588fa Debug tests. 2026-01-20 17:07:07 +01:00
Christoph Holzheuer
0d802fcec3 Changed painter, add visual studio project. 2026-01-20 16:56:41 +01:00
Christoph Holzheuer
8639529bbe Reworked value handling. 2026-01-19 16:44:52 +01:00
4309d2231e Changed setRaw Value 2026-01-18 22:47:26 +01:00
093b90fab6 Changed value handling. 2026-01-18 18:52:30 +01:00
6aec85418a Changed BCValue 2026-01-16 23:46:58 +01:00
c6c058279a Added toggle switch 2026-01-15 21:26:10 +01:00
291695bcb9 Changed triggers. 2026-01-13 22:29:56 +01:00
Christoph Holzheuer
95dd9d18e6 Added setFromDouble 2026-01-13 16:29:02 +01:00
beae1c1b3d Cleanups. 2026-01-12 23:06:36 +01:00
Christoph Holzheuer
c0ce6a81e3 Removed debug messages. 2026-01-12 09:01:06 +01:00
9b1a1233f9 Reduce Warnigns. 2026-01-12 08:04:41 +01:00
5c919e4d55 Changed styles. 2026-01-11 20:01:33 +01:00
25e752e83b Added getter & setter for BCValue 2026-01-11 14:48:51 +01:00
9c35c9ea42 Reworking value editro, part I 2026-01-11 11:37:52 +01:00
6232b560b5 Added missing files. 2026-01-11 01:13:56 +01:00
d3c62335b1 Chanded to permanent editors. 2026-01-11 01:12:28 +01:00
7780657d82 Backups. 2026-01-10 22:18:54 +01:00
aa4b2a1b84 Added smiles. 2026-01-10 16:38:52 +01:00
cb553cf928 Added smile 2026-01-10 16:37:59 +01:00
dc3669f513 Added gui widgets as own classes, part I 2026-01-10 14:39:43 +01:00
9f0382965f Added debug timer. 2026-01-09 17:57:39 +01:00
653aa49a7b Updated. 2026-01-09 10:58:25 +01:00
2547ed6e1c Driver fixes. 2026-01-09 10:47:29 +01:00
c81c38f780 Hotfix. 2026-01-09 08:43:28 +01:00
1fc551d7d1 Removed debug code. 2026-01-09 08:39:53 +01:00
53b4d6e041 Cleanups. 2026-01-09 06:19:37 +01:00
95765226e9 Graphic updates. 2026-01-09 00:45:26 +01:00
d6da7aac9a Fixed Flag handling. 2026-01-08 20:47:05 +01:00
6b03797600 Fixed thread sync 2026-01-08 19:05:07 +01:00
Christoph Holzheuer
f19a33cc5f Debug updates. 2026-01-08 14:55:47 +01:00
c40f288aaa -- fy 2026-01-08 00:25:36 +01:00
23695bc7ef Fixed light & dark modes. 2026-01-07 22:20:39 +01:00
Christoph Holzheuer
3bdc491830 Updated ValueHandling. 2026-01-07 17:13:35 +01:00
7d43b0a694 Changed icons. 2026-01-06 23:06:29 +01:00
407e2e41ed add png versions of png icons 2026-01-06 22:45:50 +01:00
398d50c45c Added gemini_dark qss file. 2026-01-06 22:44:36 +01:00
4dd278dbf7 Added new DarkMode style. 2026-01-06 22:39:41 +01:00
be44a70a57 Added, again, missing files. sourcetree sucks! 2026-01-06 20:25:02 +01:00
a325cc3826 Switched to .png icons. 2026-01-06 20:22:12 +01:00
6f5b8d8df6 Converted icons to png 2026-01-06 20:06:48 +01:00
50c82bca43 Style updates. 2026-01-06 19:52:55 +01:00
a8a947ff0b Fixing button locking, part I 2026-01-06 18:47:08 +01:00
78 changed files with 10744 additions and 4217 deletions

47
.gitignore vendored
View File

@@ -1,22 +1,25 @@
BionxControl.pro.user
build/
bcvalue.cpp.autosave
.qtcreator/BionxControl.pro.user
.user
# Objektdateien ignorieren
*.o
.qtc_clangd/
# Von Qt generierte MOC-Dateien (Meta-Object Compiler) ignorieren
moc_*
# Von Qt generierte Ressourcen-Dateien ignorieren
qrc_*
BionxControl
Makefile
ui_*
.qmake.stash
BionxControl.pro.user
build/
bcvalue.cpp.autosave
.qtcreator/BionxControl.pro.user
.user
# Objektdateien ignorieren
*.o
.qtc_clangd/
# Von Qt generierte MOC-Dateien (Meta-Object Compiler) ignorieren
moc_*
# Von Qt generierte Ressourcen-Dateien ignorieren
qrc_*
BionxControl
Makefile
ui_*
.qmake.stash
.vs/
debug/
release/

View File

@@ -37,6 +37,7 @@ li
windows
{
#LIBS += -L$$PWD/can_api -lmhstcan -lAdvapi32
message("Konfiguration für Windows.")
}
# You can make your code fail to compile if it uses deprecated APIs.
@@ -44,16 +45,19 @@ windows
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
aalegacy.cpp \
bc.cpp \
bcanimateddelegate.cpp \
bcdelightpmwidget.cpp \
bcdeviceview.cpp \
bcdriver.cpp \
bcdriverstatewidget.cpp \
bcdrivertinycan.cpp \
bcguihelpers.cpp \
bcsliderstyle.cpp \
bcthemeswitchbutton.cpp \
bctoggleswitch.cpp \
bctransmitter.cpp \
bcvalue.cpp \
bcvaluedelegate.cpp \
bcvalueeditor.cpp \
bcvaluemodel.cpp \
bcxmlloader.cpp \
libwin/can_drv_win.c \
@@ -63,20 +67,25 @@ SOURCES += \
HEADERS += \
bc.h \
bcanimateddelegate.h \
bcdelightpmwidget.h \
bcdeviceview.h \
bcdriver.h \
bcdriverstatewidget.h \
bcdrivertinycan.h \
bcguihelpers.h \
bcmainwindow.h \
bcsliderstyle.h \
bcthemeswitchbutton.h \
bctoggleswitch.h \
bctransmitter.h \
bcvalue.h \
bcvaluedelegate.h \
bcvalueeditor.h \
bcvaluemodel.h \
bcxmlloader.h
FORMS += \
bcmainwindow.ui
bcmainwindow.ui \
bcvalueeditor.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin

7483
BionxControl.qtvscr Normal file

File diff suppressed because one or more lines are too long

6
BionxControl.slnx Normal file
View File

@@ -0,0 +1,6 @@
<Solution>
<Configurations>
<Platform Name="x64" />
</Configurations>
<Project Path="BionxControl.vcxproj" Id="4294daef-a666-3d4c-a433-ace7fb2b0c2f" />
</Solution>

299
BionxControl.vcxproj Normal file
View File

@@ -0,0 +1,299 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{4294DAEF-A666-3D4C-A433-ACE7FB2B0C2F}</ProjectGuid>
<RootNamespace>BionxControl</RootNamespace>
<Keyword>QtVS_v304</Keyword>
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.26100.0</WindowsTargetPlatformMinVersion>
<QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<PlatformToolset>v145</PlatformToolset>
<OutputDirectory>release\</OutputDirectory>
<ATLMinimizesCRunTimeLibraryUsage>false</ATLMinimizesCRunTimeLibraryUsage>
<CharacterSet>NotSet</CharacterSet>
<ConfigurationType>Application</ConfigurationType>
<IntermediateDirectory>release\</IntermediateDirectory>
<PrimaryOutput>BionxControl</PrimaryOutput>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<PlatformToolset>v145</PlatformToolset>
<OutputDirectory>debug\</OutputDirectory>
<ATLMinimizesCRunTimeLibraryUsage>false</ATLMinimizesCRunTimeLibraryUsage>
<CharacterSet>NotSet</CharacterSet>
<ConfigurationType>Application</ConfigurationType>
<IntermediateDirectory>debug\</IntermediateDirectory>
<PrimaryOutput>BionxControl</PrimaryOutput>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(QtMsBuild)\qt_defaults.props" Condition="Exists('$(QtMsBuild)\qt_defaults.props')" />
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<QtInstall>6.10.1_msvc2022_64</QtInstall>
<QtModules>core;gui;widgets;svg</QtModules>
</PropertyGroup>
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<QtInstall>6.10.1_msvc2022_64</QtInstall>
<QtModules>core;gui;widgets;svg</QtModules>
</PropertyGroup>
<Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') OR !Exists('$(QtMsBuild)\Qt.props')">
<Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." />
</Target>
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
<Import Project="$(QtMsBuild)\Qt.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
<Import Project="$(QtMsBuild)\Qt.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">release\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">release\</IntDir>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">BionxControl</TargetName>
<IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</IgnoreImportLibrary>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">debug\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">debug\</IntDir>
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">BionxControl</TargetName>
<IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</IgnoreImportLibrary>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<AdditionalIncludeDirectories>GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;libwin;release;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -permissive- -Zc:__cplusplus -Zc:externConstexpr -std=c++23 -utf-8 -w34100 -w34189 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
<AssemblerListingLocation>release\</AssemblerListingLocation>
<BrowseInformation>false</BrowseInformation>
<DebugInformationFormat>None</DebugInformationFormat>
<DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ExceptionHandling>Sync</ExceptionHandling>
<LanguageStandard>stdcpplatest</LanguageStandard>
<ObjectFileName>release\</ObjectFileName>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessToFile>false</PreprocessToFile>
<ProgramDataBaseFileName>
</ProgramDataBaseFileName>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
<UseFullPaths>false</UseFullPaths>
<WarningLevel>Level3</WarningLevel>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<AdditionalDependencies>$(QTDIR)\lib\Qt6EntryPoint.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
<DataExecutionPrevention>true</DataExecutionPrevention>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<GenerateDebugInformation>false</GenerateDebugInformation>
<IgnoreImportLibrary>true</IgnoreImportLibrary>
<LinkIncremental>false</LinkIncremental>
<OptimizeReferences>true</OptimizeReferences>
<OutputFile>$(OutDir)\BionxControl.exe</OutputFile>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
<SubSystem>Windows</SubSystem>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Link>
<Midl>
<DefaultCharType>Unsigned</DefaultCharType>
<EnableErrorChecks>None</EnableErrorChecks>
<WarningLevel>0</WarningLevel>
</Midl>
<ResourceCompile>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<QtMoc>
<CompilerFlavor>msvc</CompilerFlavor>
<Include>./$(Configuration)/moc_predefs.h</Include>
<ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription>
<DynamicSource>output</DynamicSource>
<QtMocDir>$(Configuration)</QtMocDir>
<QtMocFileName>moc_%(Filename).cpp</QtMocFileName>
</QtMoc>
<QtRcc>
<InitFuncName>bionxcontrol</InitFuncName>
<Compression>default</Compression>
<NoZstd>true</NoZstd>
<ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription>
<QtRccDir>$(Configuration)</QtRccDir>
<QtRccFileName>qrc_%(Filename).cpp</QtRccFileName>
</QtRcc>
<QtUic>
<ExecutionDescription>Uic'ing %(Identity)...</ExecutionDescription>
<QtUicDir>$(ProjectDir)</QtUicDir>
<QtUicFileName>ui_%(Filename).h</QtUicFileName>
</QtUic>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<AdditionalIncludeDirectories>GeneratedFiles\$(ConfigurationName);GeneratedFiles;.;libwin;debug;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -permissive- -Zc:__cplusplus -Zc:externConstexpr -std=c++23 -utf-8 -w34100 -w34189 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
<AssemblerListingLocation>debug\</AssemblerListingLocation>
<BrowseInformation>false</BrowseInformation>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ExceptionHandling>Sync</ExceptionHandling>
<LanguageStandard>stdcpplatest</LanguageStandard>
<ObjectFileName>debug\</ObjectFileName>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessToFile>false</PreprocessToFile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
<UseFullPaths>false</UseFullPaths>
<WarningLevel>Level3</WarningLevel>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<AdditionalDependencies>$(QTDIR)\lib\Qt6EntryPointd.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
<DataExecutionPrevention>true</DataExecutionPrevention>
<GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreImportLibrary>true</IgnoreImportLibrary>
<OutputFile>$(OutDir)\BionxControl.exe</OutputFile>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
<SubSystem>Windows</SubSystem>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Link>
<Midl>
<DefaultCharType>Unsigned</DefaultCharType>
<EnableErrorChecks>None</EnableErrorChecks>
<WarningLevel>0</WarningLevel>
</Midl>
<ResourceCompile>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<QtMoc>
<CompilerFlavor>msvc</CompilerFlavor>
<Include>./$(Configuration)/moc_predefs.h</Include>
<ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription>
<DynamicSource>output</DynamicSource>
<QtMocDir>$(Configuration)</QtMocDir>
<QtMocFileName>moc_%(Filename).cpp</QtMocFileName>
</QtMoc>
<QtRcc>
<InitFuncName>bionxcontrol</InitFuncName>
<Compression>default</Compression>
<NoZstd>true</NoZstd>
<ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription>
<QtRccDir>$(Configuration)</QtRccDir>
<QtRccFileName>qrc_%(Filename).cpp</QtRccFileName>
</QtRcc>
<QtUic>
<ExecutionDescription>Uic'ing %(Identity)...</ExecutionDescription>
<QtUicDir>$(ProjectDir)</QtUicDir>
<QtUicFileName>ui_%(Filename).h</QtUicFileName>
</QtUic>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="bc.cpp" />
<ClCompile Include="bcdelightpmwidget.cpp" />
<ClCompile Include="bcdeviceview.cpp" />
<ClCompile Include="bcdriver.cpp" />
<ClCompile Include="bcdriverstatewidget.cpp" />
<ClCompile Include="bcdrivertinycan.cpp" />
<ClCompile Include="bcmainwindow.cpp" />
<ClCompile Include="bcsliderstyle.cpp" />
<ClCompile Include="bcthemeswitchbutton.cpp" />
<ClCompile Include="bctoggleswitch.cpp" />
<ClCompile Include="bctransmitter.cpp" />
<ClCompile Include="bcvalue.cpp" />
<ClCompile Include="bcvaluedelegate.cpp" />
<ClCompile Include="bcvalueeditor.cpp" />
<ClCompile Include="bcvaluemodel.cpp" />
<ClCompile Include="bcxmlloader.cpp" />
<ClCompile Include="libwin\can_drv_win.c" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<QtMoc Include="bc.h" />
<QtMoc Include="bcdelightpmwidget.h" />
<QtMoc Include="bcdeviceview.h" />
<QtMoc Include="bcdriver.h" />
<QtMoc Include="bcdriverstatewidget.h" />
<ClInclude Include="bcdrivertinycan.h" />
<QtMoc Include="bcmainwindow.h" />
<ClInclude Include="bcsliderstyle.h" />
<QtMoc Include="bcthemeswitchbutton.h" />
<QtMoc Include="bctoggleswitch.h" />
<QtMoc Include="bctransmitter.h" />
<QtMoc Include="bcvalue.h" />
<QtMoc Include="bcvaluedelegate.h" />
<QtMoc Include="bcvalueeditor.h" />
<QtMoc Include="bcvaluemodel.h" />
<QtMoc Include="bcxmlloader.h" />
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">
<FileType>Document</FileType>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs)</AdditionalInputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -permissive- -Zc:__cplusplus -Zc:externConstexpr -std=c++23 -Zi -MDd -std:c++latest -utf-8 -W3 -w34100 -w34189 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2&gt;NUL &gt;$(IntDir)\moc_predefs.h</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Generate moc_predefs.h</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)\moc_predefs.h;%(Outputs)</Outputs>
</CustomBuild>
<CustomBuild Include="release\moc_predefs.h.cbt">
<FileType>Document</FileType>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs)</AdditionalInputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -permissive- -Zc:__cplusplus -Zc:externConstexpr -std=c++23 -O2 -MD -std:c++latest -utf-8 -W3 -w34100 -w34189 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2&gt;NUL &gt;$(IntDir)\moc_predefs.h</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Generate moc_predefs.h</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)\moc_predefs.h;%(Outputs)</Outputs>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<QtUic Include="bcmainwindow.ui" />
<QtUic Include="bcvalueeditor.ui" />
</ItemGroup>
<ItemGroup>
<None Include="resources\bc_dark.qss" />
<None Include="resources\bc_light.qss" />
<None Include="resources\bikeinfo.xml" />
<None Include="resources\bionx_akku.png" />
<None Include="resources\bionx_console.png" />
<None Include="resources\bionx_motor.png" />
<QtRcc Include="bionxcontrol.qrc" />
<None Include="resources\connect.png" />
<None Include="resources\exit.png" />
<None Include="resources\exit_red.png" />
<None Include="resources\smile\face-angel.png" />
<None Include="resources\smile\face-angry.png" />
<None Include="resources\smile\face-cool.png" />
<None Include="resources\smile\face-crying.png" />
<None Include="resources\smile\face-embarrassed.png" />
<None Include="resources\smile\face-glasses.png" />
<None Include="resources\smile\face-kiss.png" />
<None Include="resources\smile\face-laugh.png" />
<None Include="resources\smile\face-monkey.png" />
<None Include="resources\smile\face-plain.png" />
<None Include="resources\smile\face-raspberry.png" />
<None Include="resources\smile\face-sad.png" />
<None Include="resources\smile\face-sick.png" />
<None Include="resources\smile\face-smile-big.png" />
<None Include="resources\smile\face-smile.png" />
<None Include="resources\smile\face-smirk.png" />
<None Include="resources\smile\face-surprise.png" />
<None Include="resources\sync.png" />
<None Include="resources\sync_green.png" />
<None Include="resources\sync_yellow.png" />
<None Include="resources\update.png" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(QtMsBuild)\qt.targets" Condition="Exists('$(QtMsBuild)\qt.targets')" />
<ImportGroup Label="ExtensionTargets" />
</Project>

View File

@@ -0,0 +1,266 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Form Files">
<UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier>
<Extensions>ui</Extensions>
<ParseFiles>false</ParseFiles>
</Filter>
<Filter Include="Form Files">
<UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier>
<Extensions>ui</Extensions>
<ParseFiles>false</ParseFiles>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}</UniqueIdentifier>
<Extensions>cpp;c;cxx;moc;h;def;odl;idl;res;</Extensions>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}</UniqueIdentifier>
<Extensions>cpp;c;cxx;moc;h;def;odl;idl;res;</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}</UniqueIdentifier>
<Extensions>qrc;*</Extensions>
<ParseFiles>false</ParseFiles>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}</UniqueIdentifier>
<Extensions>qrc;*</Extensions>
<ParseFiles>false</ParseFiles>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bc.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcdelightpmwidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcdeviceview.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcdriver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcdriverstatewidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcdrivertinycan.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcmainwindow.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcsliderstyle.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcthemeswitchbutton.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bctoggleswitch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bctransmitter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcvalue.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcvaluedelegate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcvalueeditor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcvaluemodel.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bcxmlloader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="libwin\can_drv_win.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<QtMoc Include="bc.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="bcdelightpmwidget.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="bcdeviceview.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="bcdriver.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="bcdriverstatewidget.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="bcdrivertinycan.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="bcmainwindow.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="bcsliderstyle.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="bcthemeswitchbutton.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="bctoggleswitch.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="bctransmitter.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="bcvalue.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="bcvaluedelegate.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="bcvalueeditor.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="bcvaluemodel.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="bcxmlloader.h">
<Filter>Header Files</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">
<Filter>Generated Files</Filter>
</CustomBuild>
<CustomBuild Include="release\moc_predefs.h.cbt">
<Filter>Generated Files</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<QtUic Include="bcmainwindow.ui">
<Filter>Form Files</Filter>
</QtUic>
<QtUic Include="bcvalueeditor.ui">
<Filter>Form Files</Filter>
</QtUic>
</ItemGroup>
<ItemGroup>
<None Include="resources\bc_dark.qss">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\bc_light.qss">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\bikeinfo.xml">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\bionx_akku.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\bionx_console.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\bionx_motor.png">
<Filter>Resource Files</Filter>
</None>
<QtRcc Include="bionxcontrol.qrc">
<Filter>Resource Files</Filter>
</QtRcc>
<None Include="resources\connect.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\exit.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\exit_red.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-angel.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-angry.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-cool.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-crying.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-embarrassed.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-glasses.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-kiss.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-laugh.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-monkey.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-plain.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-raspberry.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-sad.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-sick.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-smile-big.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-smile.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-smirk.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\smile\face-surprise.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\sync.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\sync_green.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\sync_yellow.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\update.png">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
</Project>

12
BionxControl.vcxproj.user Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<QtTouchProperty>
</QtTouchProperty>
</PropertyGroup>
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<QtTouchProperty>
</QtTouchProperty>
</PropertyGroup>
</Project>

View File

@@ -1,744 +0,0 @@
/* BigXionFlasher.c */
/* ====================================================================
* Copyright (c) 2011-2013 by Thomas König <info@bigxionflasher.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the
* BigXionFlasher Project. (http://www.bigxionflasher.org/)"
*
* 4. The name "BigXionFlasher" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* info@bigxionflasher.org.
*
* 5. Products derived from this software may not be called "BigXionFlasher"
* nor may "BigXionFlasher" appear in their names without prior written
* permission of the BigXionFlasher Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the
* BigXionFlasher Project. (http://www.bigxionflasher.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE BigXionFlasher PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE BigXionFlasher PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#ifdef __WIN32__
#include <conio.h>
#define DEVICE_OPEN NULL
#define TREIBER_NAME "mhstcan.dll"
#define _NL "\n\r"
#define _DEGREE_SIGN "o"
#else
#define DEVICE_OPEN NULL
#define TREIBER_NAME "libmhstcan.so"
#define _NL "\n"
#define _DEGREE_SIGN "°"
#endif
#include "can_drv.h"
#define __DOSTR(v) #v
#define __STR(v) __DOSTR(v)
#define __BXF_VERSION__ "V 0.2.4 rev. 97"
#define UNLIMITED_SPEED_VALUE 70 /* Km/h */
#define UNLIMITED_MIN_SPEED_VALUE 30 /* Km/h */
#define MAX_THROTTLE_SPEED_VALUE 70 /* Km/h */
//#include "registers.h"
#define TIMEOUT_VALUE 80
#define TIMEOUT_US 10000 // 10ms
#define BATTERY_REF_HW 1
#define BATTERY_REF_SW 1
#define BATTERY_SN_PN_HI 1
#define BATTERY_SN_PN_LO 1
#define BATTERY_SN_ITEM_HI 1
#define BATTERY_SN_ITEM_LO 1
#define BATTERY_STATUS_VBATT_HI 1
#define BATTERY_STATUS_VBATT_LO 1
#define BATTERY_STATUS_LEVEL 1
#define BATTERY_STATS_VBATTMAX 1
#define BATTERY_STATS_VBATTMIN 1
#define BATTERY_STATS_VBATTMEA 1
#define BATTERY_STATS_VBATTMEAN 1
#define BATTERY_STATS_RESET_HI 1
#define BATTERY_STATS_RESET_LO 1
#define BATTERY_STSTS_GGJSRCALIB 1
#define BATTERY_STSTS_VCTRLSHORTS 1
#define BATTERY_STATS_LMD_HI 1
#define BATTERY_STATS_LMD_LO 1
#define BATTERY_CONFIG_CELLCAPACITY_HI 1
#define BATTERY_CONFIG_CELLCAPACITY_LO 1
#define BATTERY_STATS_CHARGETIMEWORST_HI 1
#define BATTERY_STATS_CHARGETIMEWORST_LO 1
#define BATTERY_STATS_CHARGETIMEMEAN_HI 1
#define BATTERY_STATS_CHARGETIMEMEAN_LO 1
#define BATTERY_STATS_BATTCYCLES_HI 1
#define BATTERY_STATS_BATTCYCLES_LO 1
#define BATTERY_STATS_BATTFULLCYCLES_HI 1
#define BATTERY_STATS_BATTFULLCYCLES_LO 1
#define BATTERY_STATS_POWERCYCLES_HI 1
#define BATTERY_STATS_POWERCYCLES_LO 1
#define BATTERY_STATS_TBATTMAX 1
#define BATTERY_STATS_TBATTMIN 1
#define MOTOR_REF_HW 1
#define MOTOR_REF_SW 1
#define MOTOR_REALTIME_TEMP 1
#define MOTOR_SN_PN_HI 1
#define MOTOR_SN_PN_LO 1
#define MOTOR_SN_ITEM_HI 1
#define MOTOR_SN_ITEM_LO 1
#define CONSOLE_STATUS_SLAVE 1
#define BATTERY_CONFIG_SHUTDOWN 1
#define CONSOLE_ASSIST_MAXSPEEDFLAG 1
#define CONSOLE_ASSIST_MAXSPEED_HI 1
#define CONSOLE_ASSIST_MAXSPEED_LO 1
#define MOTOR_PROTECT_UNLOCK 1
#define MOTOR_PROTECT_UNLOCK_KEY 1
#define MOTOR_ASSIST_MAXSPEED 1
#define CONSOLE_GEOMETRY_CIRC_HI 1
#define CONSOLE_GEOMETRY_CIRC_LO1
#define CONSOLE_GEOMETRY_CIRC_LO 1
#define MOTOR_GEOMETRY_CIRC_HI1
#define MOTOR_GEOMETRY_CIRC_HI 1
#define MOTOR_GEOMETRY_CIRC_LO 1
#define CONSOLE_ASSIST_MINSPEEDFLAG 1
#define CONSOLE_ASSIST_MINSPEED 1
#define CONSOLE_THROTTLE_MAXSPEEDFLAG 1
#define CONSOLE_THROTTLE_MAXSPEED_HI 1
#define CONSOLE_THROTTLE_MAXSPEED_LO 1
#define BATTERY_CONFIG_PACKSERIAL 1
#define BATTERY_CELLMON_BALANCERENABLED 1
#define BATTERY_CONFIG_PACKPARALLEL 1
#define BATTERY_CELLMON_CHANNELADDR 1
#define BATTERY_CELLMON_CHANNELDATA_HI 1
#define BATTERY_STATUS_PACKTEMPERATURE1 1
#define BATTERY_CELLMON_CHANNELDATA_LO 1
#define CONSOLE_REF_HW 1
#define CONSOLE_REF_SW 1
#define CONSOLE_ASSIST_INITLEVEL 1
#define CONSOLE_SN_PN_HI 1
#define CONSOLE_SN_PN_LO 1
#define CONSOLE_SN_ITEM_HI 1
#define CONSOLE_SN_ITEM_LO 1
#define CONSOLE_ASSIST_MOUNTAINCAP 1
#define CONSOLE_STATS_BCValueTypeWord_1 1
#define CONSOLE_STATS_BCValueTypeWord_2 1
#define CONSOLE_STATS_BCValueTypeWord_3 1
#define CONSOLE_STATS_BCValueTypeWord_4 1
#define doSleep(x) usleep(x*1000)
int gAssistInitLevel = -1, gPrintSystemSettings = 0, gSkipShutdown = 0, gPowerOff = 0, gConsoleSetSlaveMode = 1, gNoSerialNumbers = 0, gSetMountainCap = -1, gSetWheelCircumference = 0;
double gSetSpeedLimit = -1, gSetMinSpeedLimit = -1, gSetThrottleSpeedLimit = -1;
#define CONSOLE 1
#define MOTOR 2
#define BATTERY 3
#define BIB 4
char *getNodeName(unsigned char id)
{
if (id == CONSOLE)
return "console";
else if (id == BATTERY)
return "battery";
else if (id == MOTOR)
return "motor";
else if (id == BIB)
return "bib";
else
return "UNKNOWN";
}
void setValue(unsigned char receipient, unsigned char reg, unsigned char value)
{
struct TCanMsg msg;
int timeout = TIMEOUT_VALUE;
msg.MsgFlags = 0L;
msg.Id = receipient;
msg.MsgLen = 4;
msg.MsgData[0] = 0x00;
msg.MsgData[1] = reg;
msg.MsgData[2] = 0x00;
msg.MsgData[3] = value;
CanTransmit(0, &msg, 1);
while(timeout-- && CanTransmitGetCount(0))
usleep(TIMEOUT_US);
if (timeout == -1)
printf("error: could not send value to %s" _NL, getNodeName(receipient));
}
unsigned int getValue(unsigned char receipient, unsigned char reg)
{
struct TCanMsg msg;
int err, retry = 20;
int timeout = TIMEOUT_VALUE;
msg.MsgFlags = 0L;
msg.Id = receipient;
msg.MsgLen = 2;
msg.MsgData[0] = 0x00;
msg.MsgData[1] = reg;
CanTransmit(0, &msg, 1);
while(timeout-- && CanTransmitGetCount(0))
usleep(TIMEOUT_US);
if (timeout == -1)
printf("error: could not send value to node %s" _NL, getNodeName(receipient));
retry:
timeout = TIMEOUT_VALUE;
while(timeout-- && !CanReceiveGetCount(0))
usleep(TIMEOUT_US);
if (timeout == -1)
{
printf("error: no response from node %s" _NL, getNodeName(receipient));
return 0;
}
if ((err = CanReceive(0, &msg, 1)) > 0)
{
if (--retry && (msg.Id != BIB || msg.MsgLen != 4 || msg.MsgData[1] != reg))
goto retry;
if (!retry)
{
printf("error: no response from node %s to %s" _NL, getNodeName(receipient), getNodeName(BIB));
return 0;
}
return (unsigned int) msg.MsgData[3];
}
else
{
printf("Error: %d" _NL, err);
}
return 0;
}
void setSpeedLimit(double speed)
{
int limit = (speed != 0);
if (!speed)
speed = UNLIMITED_SPEED_VALUE;
setValue(CONSOLE, CONSOLE_ASSIST_MAXSPEEDFLAG, limit);
setValue(CONSOLE, CONSOLE_ASSIST_MAXSPEED_HI, ((int)(speed * 10)) >> 8);
setValue(CONSOLE, CONSOLE_ASSIST_MAXSPEED_LO, ((int)(speed * 10)) & 0xff);
setValue(MOTOR, MOTOR_PROTECT_UNLOCK, MOTOR_PROTECT_UNLOCK_KEY);
setValue(MOTOR, MOTOR_ASSIST_MAXSPEED, (int)speed);
}
void setWheelCircumference(unsigned short circumference)
{
if (!circumference)
return;
setValue(CONSOLE, CONSOLE_GEOMETRY_CIRC_HI, (int) (circumference >> 8));
setValue(CONSOLE, CONSOLE_GEOMETRY_CIRC_LO, (int) (circumference & 0xff));
setValue(MOTOR, MOTOR_PROTECT_UNLOCK, MOTOR_PROTECT_UNLOCK_KEY);
setValue(MOTOR, MOTOR_GEOMETRY_CIRC_HI, (int) (circumference >> 8));
setValue(MOTOR, MOTOR_GEOMETRY_CIRC_LO, (int) (circumference & 0xff));
}
void setMinSpeedLimit(double speed)
{
char limit = (speed != 0);
setValue(CONSOLE, CONSOLE_ASSIST_MINSPEEDFLAG, limit);
setValue(CONSOLE, CONSOLE_ASSIST_MINSPEED, (int)(speed * 10));
}
void setThrottleSpeedLimit(double speed)
{
int limit = (speed != 0);
if (!speed)
speed = MAX_THROTTLE_SPEED_VALUE;
setValue(CONSOLE, CONSOLE_THROTTLE_MAXSPEEDFLAG, limit);
setValue(CONSOLE, CONSOLE_THROTTLE_MAXSPEED_HI, ((int)(speed * 10)) >> 8);
setValue(CONSOLE, CONSOLE_THROTTLE_MAXSPEED_LO, ((int)(speed * 10)) & 0xff);
}
void printBatteryStats()
{
int channel = 1, packSerial, packParallel;
printf( " balancer enabled ...: %s" _NL _NL, (getValue(BATTERY, BATTERY_CELLMON_BALANCERENABLED != 0) ? "yes" : "no"));
packSerial = getValue(BATTERY, BATTERY_CONFIG_PACKSERIAL);
packParallel = getValue(BATTERY, BATTERY_CONFIG_PACKPARALLEL);
packSerial = (packSerial > 20) ? 0 : packSerial;
packParallel = (packParallel > 20) ? 0 : packParallel;
for (;channel <= packSerial; channel++) {
setValue(BATTERY, BATTERY_CELLMON_CHANNELADDR, (int)0x80 + channel);
printf(" voltage cell #%02d ...: %.3fV" _NL, channel,
((getValue(BATTERY, BATTERY_CELLMON_CHANNELDATA_HI) << 8) + getValue(BATTERY,BATTERY_CELLMON_CHANNELDATA_LO)) * 0.001);
}
for (channel = 0 ; channel < packParallel ; channel ++)
printf(" temperature pack #%02d: %d" _DEGREE_SIGN "C" _NL, channel + 1,
getValue(BATTERY, BATTERY_STATUS_PACKTEMPERATURE1 + channel));
printf(_NL);
}
void printChargeStats() {
int channel = 1, totalChagres = 0, c;
for (channel = 1 ; channel <= 10; channel++) {
setValue(BATTERY, 0xf6, channel);
c = (getValue(BATTERY, 0xf7) << 8) + getValue(BATTERY,0xf8);
totalChagres += c;
printf(" charge level @ %03d%% : %04d" _NL, channel*10, c);
}
printf(" total # of charges .: %04d" _NL _NL, totalChagres);
}
double getVoltageValue(unsigned char in, unsigned char reg)
{
return (getValue(BATTERY, reg) + 20.8333) * 0.416667;
}
void usage(void) {
printf( "usage:" _NL
" -l <speedLimit> .......... set the speed limit to <speedLimit> (1 - " __STR(UNLIMITED_SPEED_VALUE) "), 0 = remove the limit" _NL );
}
int parseOptions(int argc, char **argv)
{
int oc;
char odef[] = "l:t:m:sa:pnxio:c:h?";
while((oc = getopt(argc,argv,odef)) != -1) {
switch(oc) {
case 'p':
gPowerOff = 1;
break;
case 'x':
gSkipShutdown = 1;
break;
case 'l':
gSetSpeedLimit = atof(optarg);
if (gSetSpeedLimit > UNLIMITED_SPEED_VALUE || gSetSpeedLimit < 0) {
printf("error: speed limit %.2f is out of range. exiting..." _NL, gSetSpeedLimit);
return -1;
}
break;
case 't':
gSetThrottleSpeedLimit = atof(optarg);
if (gSetThrottleSpeedLimit > MAX_THROTTLE_SPEED_VALUE || gSetThrottleSpeedLimit < 0) {
printf("error: throttle speed limit %.2f is out of range. exiting..." _NL, gSetThrottleSpeedLimit);
return -1;
}
break;
case 'm':
gSetMinSpeedLimit = atof(optarg);
if (gSetMinSpeedLimit > UNLIMITED_MIN_SPEED_VALUE || gSetMinSpeedLimit < 0) {
printf("error: min speed limit %.2f is out of range. exiting..." _NL, gSetMinSpeedLimit);
return -1;
}
break;
case 'a':
gAssistInitLevel = atoi(optarg);
if (gAssistInitLevel > 4 || gAssistInitLevel < 0) {
printf("error: initial assist level %d is out of range. exiting..." _NL, gAssistInitLevel);
return -1;
}
break;
case 'o':
gSetMountainCap = atoi(optarg);
if (gSetMountainCap > 100 || gSetMountainCap < 0) {
printf("error: mountain cap level %d is out of range. exiting..." _NL, gSetMountainCap);
return -1;
}
break;
case 'c':
gSetWheelCircumference = atoi(optarg);
if (gSetWheelCircumference > 3000 || gSetWheelCircumference < 1000) {
printf("error: wheel circumference %d is out of range. exiting..." _NL, gSetWheelCircumference);
return -1;
}
break;
case 'n':
gConsoleSetSlaveMode = 0;
break;
case 'i':
gNoSerialNumbers = 1;
break;
case 's':
gPrintSystemSettings = 1;
break;
case 'h':
case '?':
default:
usage();
return -1;
}
}
return 0;
}
void printSystemSettings()
{
int hwVersion, swVersion, wheelCirc;
char *sl;
double speedLimit = 0;
printf(_NL _NL);
hwVersion = getValue(CONSOLE, CONSOLE_REF_HW);
if (hwVersion == 0)
printf("Console not responding" _NL _NL);
else {
swVersion = getValue(CONSOLE, CONSOLE_REF_SW);
printf( "Console information:" _NL
" hardware version ........: %02d" _NL
" software version ........: %02d" _NL
" assistance level ........: %d" _NL,
hwVersion, swVersion,
getValue(CONSOLE, CONSOLE_ASSIST_INITLEVEL)
);
if (!gNoSerialNumbers)
printf( " part number .............: %05d" _NL
" item number .............: %05d" _NL _NL,
((getValue(CONSOLE, CONSOLE_SN_PN_HI) << 8) + getValue(CONSOLE, CONSOLE_SN_PN_LO)),
((getValue(CONSOLE, CONSOLE_SN_ITEM_HI) << 8) + getValue(CONSOLE, CONSOLE_SN_ITEM_LO))
);
/* ASSIST speed limit */
sl = getValue(CONSOLE, CONSOLE_ASSIST_MAXSPEEDFLAG) == 0 ? (char*)"no" : (char*)"yes";
speedLimit = ((getValue(CONSOLE, CONSOLE_ASSIST_MAXSPEED_HI) << 8) + getValue(CONSOLE, CONSOLE_ASSIST_MAXSPEED_LO)) / (double)10;
printf( " max limit enabled .......: %s" _NL
" speed limit .............: %0.2f Km/h" _NL _NL, sl, speedLimit);
/* MIN speed limit */
sl = getValue(CONSOLE, CONSOLE_ASSIST_MINSPEEDFLAG) == 0 ? (char*)"no" : (char*)"yes";
speedLimit = (getValue(CONSOLE, CONSOLE_ASSIST_MINSPEED)) / (double)10;
printf( " min limit enabled .......: %s" _NL
" min speed limit .........: %0.2f Km/h" _NL _NL, sl, speedLimit);
/* THROTTLE speed limit */
sl = getValue(CONSOLE, CONSOLE_THROTTLE_MAXSPEEDFLAG) == 0 ? (char*)"no" : (char*)"yes";
speedLimit = ((getValue(CONSOLE, CONSOLE_THROTTLE_MAXSPEED_HI) << 8) + getValue(CONSOLE, CONSOLE_THROTTLE_MAXSPEED_LO)) / (double)10;
printf( " throttle limit enabled ..: %s" _NL
" throttle speed limit ....: %0.2f Km/h" _NL _NL, sl, speedLimit);
/* WHEEL CIRCUMFERENCE */
wheelCirc = (getValue(CONSOLE, CONSOLE_GEOMETRY_CIRC_HI) << 8) + getValue(CONSOLE, CONSOLE_GEOMETRY_CIRC_LO);
printf( " wheel circumference .....: %d mm" _NL _NL, wheelCirc);
if (swVersion >= 59)
printf(
" mountain cap ............: %0.2f%%" _NL,
(getValue(CONSOLE, CONSOLE_ASSIST_MOUNTAINCAP) * 1.5625));
printf( " odo .....................: %0.2f Km" _NL _NL,
((getValue(CONSOLE, CONSOLE_STATS_BCValueTypeWord_1) << 24) +
(getValue(CONSOLE, CONSOLE_STATS_BCValueTypeWord_2) << 16) +
(getValue(CONSOLE, CONSOLE_STATS_BCValueTypeWord_3) << 8) +
(getValue(CONSOLE, CONSOLE_STATS_BCValueTypeWord_4))) / (double)10
);
}
hwVersion = getValue(BATTERY, BATTERY_REF_HW);
if (hwVersion == 0)
printf("Battery not responding" _NL _NL);
else {
printf( "Battery information:" _NL
" hardware version ........: %02d" _NL
" software version ........: %02d" _NL,
hwVersion, getValue(BATTERY, BATTERY_REF_SW)
);
if (!gNoSerialNumbers)
printf( " part number .............: %05d" _NL
" item number .............: %05d" _NL,
((getValue(BATTERY, BATTERY_SN_PN_HI) << 8) + getValue(BATTERY, BATTERY_SN_PN_LO)),
((getValue(BATTERY, BATTERY_SN_ITEM_HI) << 8) + getValue(BATTERY, BATTERY_SN_ITEM_LO))
);
printf( " voltage .................: %0.2fV" _NL
" battery level ...........: %0.2f%%" _NL
" maximum voltage .........: %0.2f%%" _NL
" minimum voltage .........: %0.2f%%" _NL
" mean voltage ............: %0.2f%%" _NL
" resets ..................: %0d" _NL
" ggjrCalib ...............: %0d" _NL
" vctrlShorts .............: %0d" _NL
" lmd .....................: %0.2fAh" _NL
" cell capacity ...........: %0.2fAh" _NL _NL,
((getValue(BATTERY, BATTERY_STATUS_VBATT_HI) << 8) + getValue(BATTERY, BATTERY_STATUS_VBATT_LO)) * 0.001,
(getValue(BATTERY, BATTERY_STATUS_LEVEL) * 6.6667),
getVoltageValue(BATTERY, BATTERY_STATS_VBATTMAX),
getVoltageValue(BATTERY, BATTERY_STATS_VBATTMIN),
getVoltageValue(BATTERY, BATTERY_STATS_VBATTMEAN),
(getValue(BATTERY, BATTERY_STATS_RESET_HI) << 8) + getValue(BATTERY, BATTERY_STATS_RESET_LO),
getValue(BATTERY, BATTERY_STSTS_GGJSRCALIB),
getValue(BATTERY, BATTERY_STSTS_VCTRLSHORTS),
((getValue(BATTERY, BATTERY_STATS_LMD_HI) << 8) + getValue(BATTERY, BATTERY_STATS_LMD_LO)) * 0.002142,
((getValue(BATTERY, BATTERY_CONFIG_CELLCAPACITY_HI) << 8) + getValue(BATTERY, BATTERY_CONFIG_CELLCAPACITY_LO)) * 0.001
);
printf( " charge time worst .......: %0d" _NL
" charge time mean ........: %0d" _NL
" charge cycles ...........: %0d" _NL
" full charge cycles ......: %0d" _NL
" power cycles ............: %0d" _NL
" battery temp max ........: %0d" _NL
" battery temp min ........: %0d" _NL _NL,
(getValue(BATTERY, BATTERY_STATS_CHARGETIMEWORST_HI) << 8) + getValue(BATTERY, BATTERY_STATS_CHARGETIMEWORST_LO),
(getValue(BATTERY, BATTERY_STATS_CHARGETIMEMEAN_HI) << 8) + getValue(BATTERY, BATTERY_STATS_CHARGETIMEMEAN_LO),
(getValue(BATTERY, BATTERY_STATS_BATTCYCLES_HI) << 8) + getValue(BATTERY, BATTERY_STATS_BATTCYCLES_LO),
(getValue(BATTERY, BATTERY_STATS_BATTFULLCYCLES_HI) << 8) + getValue(BATTERY, BATTERY_STATS_BATTFULLCYCLES_LO),
(getValue(BATTERY, BATTERY_STATS_POWERCYCLES_HI) << 8) + getValue(BATTERY, BATTERY_STATS_POWERCYCLES_HI),
getValue(BATTERY, BATTERY_STATS_TBATTMAX),
getValue(BATTERY, BATTERY_STATS_TBATTMIN)
);
printChargeStats();
if (hwVersion >= 60)
printBatteryStats();
else
printf(" no battery details supported by battery hardware #%d" _NL _NL, hwVersion);
}
hwVersion = getValue(MOTOR, MOTOR_REF_HW);
if (hwVersion == 0)
printf("Motor not responding" _NL _NL);
else {
printf( "Motor information:" _NL
" hardware version ........: %02d" _NL
" software version ........: %02d" _NL
//" temperature .............: %02d" _DEGREE_SIGN "C"_NL
" speed limit .............: %02d Km/h" _NL,
hwVersion, getValue(MOTOR, MOTOR_REF_SW),
getValue(MOTOR, MOTOR_REALTIME_TEMP),
getValue(MOTOR, MOTOR_ASSIST_MAXSPEED)
);
wheelCirc = (getValue(MOTOR, MOTOR_GEOMETRY_CIRC_HI) << 8) + getValue(MOTOR, MOTOR_GEOMETRY_CIRC_LO);
printf( " wheel circumference .....: %d mm" _NL _NL, wheelCirc);
if (!gNoSerialNumbers)
printf( " part number .............: %05d" _NL
" item number .............: %05d" _NL _NL,
((getValue(MOTOR, MOTOR_SN_PN_HI) << 8) + getValue(MOTOR, MOTOR_SN_PN_LO)),
((getValue(MOTOR, MOTOR_SN_ITEM_HI) << 8) + getValue(MOTOR, MOTOR_SN_ITEM_LO))
);
}
}
int xmain(int argc, char **argv)
{
int err, doShutdown = 0, consoleInSlaveMode = 0;
struct TDeviceStatus status;
printf("BigXionFlasher USB " __BXF_VERSION__ _NL " (c) 2011-2013 by Thomas Koenig <info@bigxionflasher.org> - www.bigxionflasher.org" _NL);
if ((err=parseOptions(argc, argv) < 0))
exit(1);
if ((err = LoadDriver(TREIBER_NAME)) < 0) {
printf("LoadDriver error: %d" _NL, err);
goto error;
}
if ((err = CanInitDriver(NULL)) < 0) {
printf("CanInitDriver error: %d" _NL, err);
goto error;
}
if ((err = CanDeviceOpen(0, DEVICE_OPEN)) < 0) {
printf("CanDeviceOpen error: %d" _NL, err);
goto error;
}
CanSetSpeed(0, CAN_125K_BIT);
CanSetMode(0, OP_CAN_START, CAN_CMD_ALL_CLEAR);
CanGetDeviceStatus(0, &status);
if (status.DrvStatus >= DRV_STATUS_CAN_OPEN)
{
if (status.CanStatus == CAN_STATUS_BUS_OFF)
{
printf("CAN Status BusOff" _NL);
CanSetMode(0, OP_CAN_RESET, CAN_CMD_NONE);
}
}
else
{
printf("error: could not open device" _NL);
goto error;
}
consoleInSlaveMode = getValue(CONSOLE, CONSOLE_STATUS_SLAVE);
if (consoleInSlaveMode)
{
printf("console already in salve mode. good!" _NL _NL);
}
else
{
if (gConsoleSetSlaveMode)
{
int retry = 20;
printf("putting console in salve mode ... ");
do {
setValue(CONSOLE, CONSOLE_STATUS_SLAVE, 1);
consoleInSlaveMode = getValue(CONSOLE, CONSOLE_STATUS_SLAVE);
usleep(200000);
} while(retry-- && !consoleInSlaveMode);
doSleep(500); // give the console some time to settle
printf("%s" _NL _NL, consoleInSlaveMode ? "done" : "failed");
}
else
{
printf("console not in slave mode" _NL _NL);
}
}
if (gAssistInitLevel != -1)
{
printf("setting initial assistance level to %d" _NL, gAssistInitLevel);
setValue(CONSOLE, CONSOLE_ASSIST_INITLEVEL, gAssistInitLevel);
}
if (gSetSpeedLimit > 0)
{
printf("set speed limit to %0.2f km/h" _NL, gSetSpeedLimit);
setSpeedLimit(gSetSpeedLimit);
doShutdown = 1;
} else if (gSetSpeedLimit == 0) {
printf("disable speed limit, drive carefully" _NL);
setSpeedLimit(0);
doShutdown = 1;
}
if (gSetMinSpeedLimit > 0) {
printf("set minimal speed limit to %0.2f km/h" _NL, gSetMinSpeedLimit);
setMinSpeedLimit(gSetMinSpeedLimit);
doShutdown = 1;
} else if (gSetMinSpeedLimit == 0) {
printf("disable minimal speed limit, drive carefully" _NL);
setMinSpeedLimit(0);
doShutdown = 1;
}
if (gSetThrottleSpeedLimit > 0) {
printf("set throttle speed limit to %0.2f km/h" _NL, gSetThrottleSpeedLimit);
setThrottleSpeedLimit(gSetThrottleSpeedLimit);
doShutdown = 1;
} else if (gSetThrottleSpeedLimit == 0) {
printf("disable throttle speed limit, drive carefully" _NL);
setThrottleSpeedLimit(0);
doShutdown = 1;
}
if (gSetMountainCap > 0) {
printf("set mountain cap level to %0.2f%%" _NL, ((int)gSetMountainCap / 1.5625) * 1.5625);
setValue(CONSOLE, CONSOLE_ASSIST_MOUNTAINCAP, gSetMountainCap / 1.5625);
}
if (gSetWheelCircumference > 0) {
printf("set wheel circumference to %d" _NL, gSetWheelCircumference);
setWheelCircumference(gSetWheelCircumference);
}
if (gPrintSystemSettings)
printSystemSettings();
if ((doShutdown && !gSkipShutdown) || gPowerOff) {
doSleep(1000);
printf("shutting down system." _NL);
setValue(BATTERY, BATTERY_CONFIG_SHUTDOWN, 1);
}
CanDownDriver();
UnloadDriver();
return 0;
error:
CanDownDriver();
UnloadDriver();
return 1;
}

71
bc.h
View File

@@ -36,9 +36,32 @@
#include <cstdint>
#include <QDebug>
#include <QObject> // Nötig für Q_GADGET/Q_ENUM Makros
#include <QTime>
//uint8_t;
#define BCTimeStamp QTime::currentTime().toString("hh:mm:ss.zzz: ")
using namespace Qt::Literals::StringLiterals; // Für _L1
namespace BCTags
{
inline constexpr auto Bike = "Bike"_L1;
inline constexpr auto Device = "Device"_L1;
inline constexpr auto ID = "ID"_L1;
inline constexpr auto Label = "Label"_L1;
inline constexpr auto UnitLabel = "UnitLabel"_L1;
inline constexpr auto IsWord = "IsWord"_L1;
inline constexpr auto ReadOnly = "ReadOnly"_L1;
inline constexpr auto Default = "Default"_L1;
inline constexpr auto Current = "Current"_L1;
inline constexpr auto Enabled = "Enabled"_L1;
inline constexpr auto ValueType = "ValueType"_L1;
inline constexpr auto Min = "Min"_L1;
inline constexpr auto Max = "Max"_L1;
inline constexpr auto Factor = "Factor"_L1;
}
/**
* @brief Simple exception class
@@ -73,15 +96,6 @@ namespace bc
[[maybe_unused]] constexpr static double NORMALIZED_VOLTAGE_OFFSET = 20.8333;
[[maybe_unused]] constexpr static double NORMALIZED_VOLTAGE_FAKTOR = 0.416667;
// misc
//#define cbc::Version "CanBusControl 0.0.01 / 02.07.2022"
[[maybe_unused]] constexpr static const char* Version = "BionxControl 0.1.00 / 08.11.2022 © 2022 chris@sourceworx.org";
[[maybe_unused]] constexpr static const char* OrgName = "source::worx";
[[maybe_unused]] constexpr static const char* DomainName = "sourceworx.org";
[[maybe_unused]] constexpr static const char* AppName = "BionxControl";
// timer
void delay_seconds( uint32_t );
void delay_millis( uint32_t );
@@ -91,27 +105,6 @@ namespace bc
QString formatInt( int count, int len );
} // namespace bc
// abbreviations:
// SOC = State Of Charge
// LMD = Last Measured Discharge
// NIP = ?
/*
Needed ?
#include <type_traits>
template <typename E>
constexpr auto to_u(E e) noexcept {
return static_cast<std::underlying_type_t<E>>(e);
}
// constants.h
#pragma once
#include <QLatin1StringView>
*/
struct BC
{
@@ -766,23 +759,5 @@ public:
Q_ENUM(ID)
};
using namespace Qt::Literals::StringLiterals; // Für _L1
namespace BCTags
{
inline constexpr auto Device = "Device"_L1;
inline constexpr auto ID = "ID"_L1;
inline constexpr auto Label = "Label"_L1;
inline constexpr auto UnitLabel = "UnitLabel"_L1;
inline constexpr auto IsWord = "IsWord"_L1;
inline constexpr auto Default = "Default"_L1;
inline constexpr auto Current = "Current"_L1;
inline constexpr auto Enabled = "Enabled"_L1;
inline constexpr auto ValueType = "ValueType"_L1;
inline constexpr auto Min = "Min"_L1;
inline constexpr auto Max = "Max"_L1;
inline constexpr auto Factor = "Factor"_L1;
}
#endif // BC_H

View File

@@ -1,358 +0,0 @@
/***************************************************************************
BionxControl
© 2025 -2026 christoph holzheuer
christoph.holzheuer@gmail.com
Using:
mhs_can_drv.c
© 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany
Klaus Demlehner, klaus@mhs-elektronik.de
@see www.mhs-elektronik.de
Based on Bionx data type descriptions from:
BigXionFlasher USB V 0.2.4 rev. 97
© 2011-2013 by Thomas Koenig <info@bigxionflasher.org>
@see www.bigxionflasher.org
Bionx Bike Info
© 2018 Thorsten Schmidt (tschmidt@ts-soft.de)
@see www.ts-soft.de
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
@see https://github.com/bikemike/bionx-bikeinfo
***************************************************************************/
#include <QSlider>
#include <QLabel>
#include <QHBoxLayout>
#include <QWidget>
#include <QDebug>
#include <QPainter>
#include <QTimer>
#include <QTableView>
#include <QVariantAnimation>
#include <QPropertyAnimation>
#include <QPainter>
#include "bcanimateddelegate.h"
BCAnimatedDelegate::BCAnimatedDelegate(const BCValueList& valueList, QTableView* view)
: QStyledItemDelegate{view}, _valueList{valueList}, _view{view}
{
}
/*
QString BCAnimatedDelegate::displayText(const QVariant& dataValue, const QLocale& locale) const
{
// Wir prüfen, ob im Variant unser Struct steckt
if (dataValue.canConvert<const BCValue&>())
{
const BCValue& bc = dataValue.value<const BCValue/>();
//qDebug() << " --- YES: " << bc.label;
// Hier bauen wir den String zusammen, den man sieht,
// wenn KEIN Editor offen ist.
// Format: "Label: Wert Einheit"
return QString("%1: %2 %3").arg(bc.label, bc.visibleValue, "mmX");
}
else
{
qDebug() << " --- Nö!";
}
// Fallback für normale Strings/Zahlen
return QStyledItemDelegate::displayText(dataValue, locale);
}
*/
QWidget *BCAnimatedDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const
{
QVariant rawData = index.data(Qt::EditRole);
//if (!rawData.canConvert<BCValue*>())
return QStyledItemDelegate::createEditor(parent, option, index);
/*
const BCValue& bc = *rawData.value<BCValue*>();
// Nur bei Integern den Slider-Editor bauen
if (bc.value.typeId() == QMetaType::Int)
{
QWidget *container = new QWidget(parent);
container->setAutoFillBackground(true);
QHBoxLayout *layout = new QHBoxLayout(container);
layout->setContentsMargins(4, 0, 4, 0);
layout->setSpacing(10);
// Linkes Label (Name)
QLabel *lblName = new QLabel(bc.label, container);
lblName->setFixedWidth(80);
// Slider
QSlider *slider = new QSlider(Qt::Horizontal, container);
slider->setRange(0, 100);
slider->setObjectName("slider");
// Rechtes Label (Vorschau Wert + Einheit)
QLabel *lblUnit = new QLabel(container);
lblUnit->setFixedWidth(60);
lblUnit->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
lblUnit->setObjectName("lblUnit");
layout->addWidget(lblName);
layout->addWidget(slider);
layout->addWidget(lblUnit);
// Live-Update des Labels im Editor (aber noch kein Speichern im Model)
connect(slider, &QSlider::valueChanged, this, [=](int val){
lblUnit->setText(QString("%1 %2").arg(val).arg("mm2"));
});
return container;
}
return QStyledItemDelegate::createEditor(parent, option, index);
*/
}
void BCAnimatedDelegate::setEditorData(QWidget *editor, const QModelIndex& index) const
{
// Daten vom Model in den Editor laden
const BCValue& bc = *index.data(Qt::EditRole).value<BCValue*>();
QSlider *slider = editor->findChild<QSlider*>("slider");
QLabel *lblUnit = editor->findChild<QLabel*>("lblUnit");
if (slider && lblUnit) {
bool olDriverState = slider->blockSignals(true);
slider->setValue(bc.visibleValue.toInt());
slider->blockSignals(olDriverState);
lblUnit->setText(QString("%1 %2").arg(bc.visibleValue.toInt()).arg( "mm3"));
} else {
QStyledItemDelegate::setEditorData(editor, index);
}
}
void BCAnimatedDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const
{
// Daten vom Editor zurück ins Model speichern (Beim Schließen)
QSlider *slider = editor->findChild<QSlider*>("slider");
if (slider) {
int value = slider->value();
model->setData(index, value, Qt::EditRole);
} else {
QStyledItemDelegate::setModelData(editor, model, index);
}
}
void BCAnimatedDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex& index) const
{
// __fix!
editor->setGeometry(option.rect);
}
QSize BCAnimatedDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex& index) const
{
return QStyledItemDelegate::sizeHint(option,index);
/*
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
opt.text = formatDisplayString(index);
QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
return style->sizeFromContents(QStyle::CT_ItemViewItem, &opt, QSize(), opt.widget);
*/
}
void BCAnimatedDelegate::paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
// Standard-Zeichnen (Text, Hintergrund, Selection) durchführen
QStyledItemDelegate::paint(painter, option, index);
int row = index.row();
int col = index.column();
switch (col)
{
case 1:
if( m_rowOpacities.contains(row))
paintHighlightRow(painter,option,index);
break;
case 2:
if( row>-1 && row <= _valueList.size() )
paintSliderIndicator(painter,option,index);
default:
break;
}
}
void BCAnimatedDelegate::paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
int row = index.row();
qreal opacity = m_rowOpacities.value(row);
painter->setOpacity(opacity);
// Margin von 4px
QRect itemRect = option.rect.adjusted(3, 3, -3, -3);
// Border (2px solid #2196F3)
QPen borderPen( Qt::red, 1);
painter->setPen(borderPen);
painter->setBrush(Qt::NoBrush);
painter->drawRoundedRect(itemRect, 2, 2);
painter->restore();
}
void BCAnimatedDelegate::paintSliderIndicator(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
const BCValue& valueX = *(_valueList[ index.row()].get());
int value = 50;index.model()->data(index, Qt::DisplayRole).toInt();
// Hintergrund
if (option.state & QStyle::State_Selected)
{
painter->fillRect(option.rect, option.palette.highlight());
}
else if (index.row() % 2 == 1)
{
painter->fillRect(option.rect, QColor(0xFAFAFA));
}
else
{
painter->fillRect(option.rect, Qt::white);
}
// Text und kleiner Slider-Indikator zeichnen
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
//QRect textRect = option.rect.adjusted(8, 0, -120, 0);
QRect barRect = option.rect.adjusted
(
8,
option.rect.height() / 2 - 2,
-8,
-option.rect.height() / 2 + 2
);
//QRect barRect = option.rect;
// Text
//painter->setPen(option.state & QStyle::State_Selected ? option.palette.highlightedText().color() : Qt::black);
//painter->drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft,
// QString::number(value));
// Mini Progress Bar
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(0xE0E0E0));
painter->drawRoundedRect(barRect, 2, 2);
QRect fillRect = barRect;
fillRect.setWidth(barRect.width() * value / 100);
painter->setBrush(QColor(0x0078D4));
painter->drawRoundedRect(fillRect, 2, 2);
painter->restore();
}
void BCAnimatedDelegate::onHighlightRow(int row)
{
// Alte Animation für diese Zeile stoppen falls vorhanden
if (m_rowAnimations.contains(row))
{
m_rowAnimations[row]->stop();
m_rowAnimations[row]->deleteLater();
}
// QVariantAnimation ist flexibler als QPropertyAnimation
auto* anim = new QVariantAnimation(this);
anim->setDuration(800);
anim->setStartValue(0.0);
anim->setEndValue(1.0);
// Custom Easing für Fade-in/out Effekt
anim->setEasingCurve(QEasingCurve::OutQuad);
connect(anim, &QVariantAnimation::valueChanged, this, [this, row](const QVariant& value)
{
qreal progress = value.toReal();
qreal opacity;
// Schnelles Fade-in (20%), langsames Fade-out (80%)
if (progress < 0.2) {
opacity = progress * 5.0; // 0->1 in 20%
} else {
opacity = 1.0 - ((progress - 0.2) / 0.8); // 1->0 in 80%
}
m_rowOpacities[row] = opacity;
updateRow(row);
});
connect(anim, &QVariantAnimation::finished, this, [this, row, anim]()
{
m_rowOpacities.remove(row);
m_rowAnimations.remove(row);
updateRow(row);
anim->deleteLater();
});
m_rowAnimations[row] = anim;
anim->start(QAbstractAnimation::DeleteWhenStopped);
}
// Optional: alle Highlights sofort clearen
void BCAnimatedDelegate::clearAllHighlights()
{
for(auto* anim : std::as_const(m_rowAnimations))
{
anim->stop();
anim->deleteLater();
}
m_rowAnimations.clear();
m_rowOpacities.clear();
if (_view)
{
_view->viewport()->update();
}
}
void BCAnimatedDelegate::updateRow(int row)
{
if (_view && _view->model() && row >= 0)
{
QModelIndex idx = _view->model()->index(row,1);
QRect rect = _view->visualRect(idx);
if (!rect.isEmpty()) {
_view->viewport()->update(rect);
}
}
}

186
bcdelightpmwidget.cpp Normal file
View File

@@ -0,0 +1,186 @@
#include <QPropertyAnimation>
#include <QSequentialAnimationGroup>
#include <QParallelAnimationGroup>
#include <QRandomGenerator>
#include <QTimer>
#include <QDebug>
#include <QGraphicsOpacityEffect>
#include <QPainterPath>
#include <QDir>
#include <QFileInfo>
#include <QIcon>
#include <QDirIterator>
#include <QPushButton>
#include <bcdelightpmwidget.h>
BCDelightPMWidget::BCDelightPMWidget(QWidget *parent)
: QObject(parent), _playGround{parent}
{
loadWidgetsFromResources();
}
// Die Methode zum automatischen Einlesen
void BCDelightPMWidget::loadWidgetsFromResources()
{
QString resourcePath = ":/resources/smile";
QDirIterator it(resourcePath, QDir::Files);
while (it.hasNext())
{
QString fullPath = it.next();
// Eine Zufallsfarbe für den Button-Hintergrund generieren
QStringList colors = {"#ff5555", "#50fa7b", "#8be9fd", "#ffb86c"};
// Ihre Funktion aufrufen und den Pfad übergeben
createFlyingWidget(fullPath);
}
}
void BCDelightPMWidget::createFlyingWidget(const QString& iconPath)
{
// 1. Button als Kind des Playground erstellen
QPushButton *btn = new QPushButton(_playGround);
// 2. Das Icon laden und setzen
QPixmap pixmap(iconPath);
QIcon icon(pixmap);
btn->setIcon(icon);
// 3. WICHTIG: Icon-Größe und Button-Größe synchronisieren
// Damit das Bild den Button voll ausfüllt
QSize size(128, 128);
btn->setFixedSize(size); // Die Klick-Fläche
btn->setIconSize(size); // Das Bild darin
// 4. Stylesheet: Alle Standard-Rahmen und Hintergründe entfernen
// "border: none" entfernt den 3D-Rahmen
// "background: transparent" macht den Rest unsichtbar
btn->setStyleSheet(
"QPushButton {"
" border: none;"
" background-color: transparent;"
" outline: none;" /* Entfernt den Fokus-Rahmen beim Klicken */
"}"
// Optional: Kleiner visueller Effekt beim Drücken (Bild bewegt sich leicht)
"QPushButton:pressed {"
" padding-top: 4px;"
" padding-left: 4px;"
"}"
);
btn->show();
QGraphicsOpacityEffect *opacityEff = new QGraphicsOpacityEffect(btn);
opacityEff->setOpacity(1.0);
btn->setGraphicsEffect(opacityEff);
// --------------------------------------
btn->show();
btn->move(50 + _flyingWidgets.size() * 60, 50);
_flyingWidgets.append(btn);
btn->move(_playGround->width()/2, _playGround->height()/2);
opacityEff->setOpacity(0.0);
}
void BCDelightPMWidget::onStartChaos()
{
// Master-Gruppe, damit alle Widgets gleichzeitig starten
QParallelAnimationGroup *masterGroup = new QParallelAnimationGroup(this);
// Gemeinsamer Startpunkt berechnen (Mitte des Playgrounds)
// Wir ziehen die halbe Größe eines Widgets (ca. 25px) ab, damit sie wirklich zentriert sind
int centerX = (_playGround->width() / 2) - 25;
int centerY = (_playGround->height() / 2) - 25;
QPoint startPoint(centerX, centerY);
for (QWidget *widget : std::as_const(_flyingWidgets))
{
QParallelAnimationGroup *widgetGroup = new QParallelAnimationGroup(masterGroup);
// ---------------------------------------------------------
// 1. Die Bogen-Animation (QVariantAnimation statt QPropertyAnimation)
// ---------------------------------------------------------
// ZIELE UND STÜTZPUNKTE BERECHNEN
int maxX = _playGround->width() - widget->width();
int maxY = _playGround->height() - widget->height();
QPoint endPoint(
QRandomGenerator::global()->bounded(qMax(0, maxX)),
QRandomGenerator::global()->bounded(qMax(0, maxY))
);
// Der Kontrollpunkt bestimmt die Kurve.
// Für eine Rakete muss er viel HÖHER liegen als Start und Ziel.
// Wir nehmen die Mitte zwischen Start/Ziel und gehen 300px nach oben (Y minus).
int controlX = (startPoint.x() + endPoint.x()) / 2;
int controlY = qMin(startPoint.y(), endPoint.y()) - 300;
// Pfad erstellen (Quadratische Bézierkurve)
QPainterPath path;
path.moveTo(startPoint);
// quadTo(Kontrollpunkt, Endpunkt)
path.quadTo(controlX, controlY, endPoint.x(), endPoint.y());
// Die Animation treibt den Fortschritt von 0.0 bis 1.0
QVariantAnimation *animCurve = new QVariantAnimation();
int duration = 2600 + QRandomGenerator::global()->bounded(800);
animCurve->setDuration(duration);
animCurve->setStartValue(0.0);
animCurve->setEndValue(1.0);
// Für ballistische Flugbahnen ist 'OutQuad' oder 'OutSine' realistisch
// (Schneller Start, oben langsamer, unten wieder schneller - physikalisch komplex,
// aber OutQuad sieht gut aus für "Wurf")
animCurve->setEasingCurve(QEasingCurve::OutQuad);
// WICHTIG: Lambda, um bei jedem Schritt die Position zu setzen
// Wir müssen 'widget' und 'path' in das Lambda capturen (by value für path ist ok)
connect(animCurve, &QVariantAnimation::valueChanged, [widget, path](const QVariant &val){
qreal progress = val.toReal();
// Magie: Berechne den Punkt auf der Kurve bei 'progress' Prozent
QPointF currentPos = path.pointAtPercent(progress);
widget->move(currentPos.toPoint());
});
widgetGroup->addAnimation(animCurve);
// ---------------------------------------------------------
// 2. Fade-Out (Bleibt fast gleich)
// ---------------------------------------------------------
QGraphicsOpacityEffect *eff = qobject_cast<QGraphicsOpacityEffect*>(widget->graphicsEffect());
if (eff) {
QPropertyAnimation *animFade = new QPropertyAnimation(eff, "opacity");
animFade->setDuration(duration);
animFade->setStartValue(1.0);
animFade->setEndValue(0.0);
// Erst am Ende ausblenden (ExpoCurve), damit man den Flugbogen sieht
animFade->setEasingCurve(QEasingCurve::InExpo);
widgetGroup->addAnimation(animFade);
}
masterGroup->addAnimation(widgetGroup);
}
// Speicher aufräumen, wenn alles vorbei ist
// Hinweis: Die Widgets bleiben danach unsichtbar (Opacity 0), existieren aber noch.
connect(masterGroup, &QAbstractAnimation::finished, masterGroup, &QObject::deleteLater);
masterGroup->start();
}

33
bcdelightpmwidget.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef BCDELIGHTPMWIDGET_H
#define BCDELIGHTPMWIDGET_H
#include <QObject>
/**
* @brief The BCDelightPMWidget class : Demonstration Graphischer
* Effekte für unseren Produktmanager Simon.
*/
class BCDelightPMWidget : public QObject
{
Q_OBJECT
public:
BCDelightPMWidget( QWidget* parent );
public slots:
void onStartChaos();
protected:
void loadWidgetsFromResources();
void createFlyingWidget(const QString& iconPath);
QWidget* _playGround{};
// Liste der Widgets, die wir bewegen
QList<QWidget*> _flyingWidgets;
};
#endif // BCDELIGHTPMWIDGET_H

View File

@@ -33,7 +33,7 @@
#include <QHeaderView>
#include <bcdeviceview.h>
#include <bcanimateddelegate.h>
#include <bcvaluedelegate.h>
BCDeviceView::BCDeviceView(QWidget *parent)
: QTableView(parent)
@@ -44,18 +44,19 @@ BCDeviceView::BCDeviceView(QWidget *parent)
//horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
// __fix! ziemlich wildes ge-pointere, hier
_itemDelegate = new BCAnimatedDelegate( _valueModel.getValueList(), this);
setItemDelegate( _itemDelegate );
_itemDelegate = new BCValueDelegate( _valueModel.getValueList(), this);
setItemDelegateForColumn( 1, _itemDelegate );
}
void BCDeviceView::setDeviceID( BCDevice::ID deviceID )
{
_devideID = deviceID;
_devideID = deviceID;
}
BCDevice::ID BCDeviceView::getDeviceID() const
BCDevice::ID BCDeviceView::deviceID() const
{
return _devideID;
}
@@ -65,21 +66,47 @@ BCDevice::ID BCDeviceView::getDeviceID() const
* @brief Gibt eine Referenz auf der ValueList zurück.
*/
const BCValueList& BCDeviceView::getValueListX()
const BCValueList& BCDeviceView::getValueList()
{
return _valueModel.getValueList();
}
/**
* @brief Flag, ob diese View schonmal angezeigt wurde.
*/
bool BCDeviceView::firstExpose()
{
bool stored = _firstExpose;
_firstExpose = false;
return stored;
}
/**
* @brief SLOT, der aufgerufen wird, wenn die ValueList vom XML-Lader fertig geladen wurde.
* Die DeviceView nimmt die ValueList dann in Besitz.
*/
void BCDeviceView::onValueListReady( BCDevice::ID deviceID, BCValueList valueList )
{
qDebug() << " --- onValueListReady: " << deviceID << ": " << valueList.size();
if(_devideID == deviceID)
{
_valueModel.takeValueList( valueList );
/*
const BCValueList& list = _valueModel.getValueList();
int rows = _valueModel.rowCount();
for (int r = 0; r < rows; ++r)
{
BCValuePtr bcValue = list[r];
if( !bcValue->isReadOnly() )
{
QModelIndex index = _valueModel.index(r, 1);
openPersistentEditor(index);
}
}
*/
} // if id
}
@@ -87,12 +114,16 @@ void BCDeviceView::onValueListReady( BCDevice::ID deviceID, BCValueList valueLis
* @brief SLOT, der aufgerufen wird, wenn ein Value geändert wurde. Gibt dem ItemDelegate Bescheid.
*/
void BCDeviceView::onValueUpdated(int index, BCValue::State state, const QString& newVisibleValue )
void BCDeviceView::updateValue(int index,BCValue::Flags newState, uint32_t rawValue )
{
_valueModel.onValueUpdated( index, state, newVisibleValue);
_valueModel.updateValue( index, newState, rawValue );
_itemDelegate->onHighlightRow( index );
}
/**
* @brief Die Spalte mit dem Label soll immer bei 60% der Gesamtbreite liegen.
* @param event
*/
void BCDeviceView::resizeEvent(QResizeEvent *event)
{

View File

@@ -38,7 +38,7 @@
#include <bcvaluemodel.h>
class BCAnimatedDelegate;
class BCValueDelegate;
class BCDeviceView : public QTableView
{
@@ -48,27 +48,27 @@ public:
explicit BCDeviceView(QWidget *parent = nullptr);
void setDeviceID( BCDevice::ID deviceID );
BCDevice::ID getDeviceID() const;
BCDevice::ID deviceID() const;
const BCValueList& getValueListX();
//BCValueModel &getValueModel();
const BCValueList& getValueList();
bool hasContent();
bool firstExpose();
void updateValue(int index, BCValue::Flags newState, uint32_t rawValue );
public slots:
void onValueListReady( BCDevice::ID deviceID, BCValueList valueList );
void onValueUpdated( int index, BCValue::State state, const QString& newVisibleValue="" );
protected:
void resizeEvent(QResizeEvent *event) override;
BCDevice::ID _devideID{BCDevice::ID::Invalid};
BCValueModel _valueModel;
BCAnimatedDelegate* _itemDelegate{};
bool _firstExpose{true};
BCDevice::ID _devideID{BCDevice::ID::Invalid};
BCValueModel _valueModel;
BCValueDelegate* _itemDelegate{};
};

View File

@@ -74,7 +74,9 @@ TransmitResult BCDriverDummy::readRawByte( uint32_t deviceID, uint8_t registerID
{
Q_UNUSED(deviceID)
Q_UNUSED(registerID)
qDebug() << " --- Dummy: readRawByte:DriverState: " << getDriverState();
// Tätigkeit simulieren
//bc::delay_millis(200);
bc::delay_millis(50);
uint8_t myRandomByte = static_cast<uint8_t>(QRandomGenerator::global()->bounded(256));
return myRandomByte;
}
@@ -88,7 +90,7 @@ TransmitResult BCDriverDummy::writeRawByte( uint32_t deviceID, uint8_t registerI
{
Q_UNUSED(deviceID)
Q_UNUSED(registerID)
qDebug() << " --- BCDriverTinyCan writeRawValue: " << value;
Q_UNUSED(value)
return 0;
}

122
bcdriverstatewidget.cpp Normal file
View File

@@ -0,0 +1,122 @@
/***************************************************************************
BionxControl
© 2025 -2026 christoph holzheuer
christoph.holzheuer@gmail.com
Using:
mhs_can_drv.c
© 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany
Klaus Demlehner, klaus@mhs-elektronik.de
@see www.mhs-elektronik.de
Based on Bionx data type descriptions from:
BigXionFlasher USB V 0.2.4 rev. 97
© 2011-2013 by Thomas Koenig <info@bigxionflasher.org>
@see www.bigxionflasher.org
Bionx Bike Info
© 2018 Thorsten Schmidt (tschmidt@ts-soft.de)
@see www.ts-soft.de
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
@see https://github.com/bikemike/bionx-bikeinfo
***************************************************************************/
#include <bcdriverstatewidget.h>
#include <QHBoxLayout>
#include <QMouseEvent>
/**
* @brief Hilfswidget: Zeigt den DriverState als Icon an.
*/
BCDriverStateWidget::BCDriverStateWidget(QWidget* parent)
: QWidget(parent)
{
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(10, 2, 10, 2);
//layout->setSpacing(8);
_led = new QLabel(this);
_led->setFixedSize(12, 12);
layout->addWidget(_led);
// Startzustand
onDriverStateChanged(BCDriver::DriverState::NotPresent, "Kein Treiber geladen.");
}
// Hauptfunktion zum Setzen des Status
// 'customMessage' ist optional. Wenn leer, wird ein Standardtext genommen.
void BCDriverStateWidget::onDriverStateChanged(BCDriver::DriverState state, const QString& customMessage)
{
Q_UNUSED(customMessage)
_state = state;
updateStyle();
}
void BCDriverStateWidget::updateStyle()
{
QString ledStyle;
QString toolTipText;
switch (_state)
{
case BCDriver::DriverState::NotPresent:
// FLUENT GRAY (Neutral)
// Wir machen es dunkelgrau mit hellem Rand -> "Ausgeschaltet"-Look
ledStyle = "background-color: #3B3B3B; border: 1px solid #606060;";
toolTipText = "Kein Treiber geladen.";
break;
case BCDriver::DriverState::Error:
// FLUENT RED (Critical)
ledStyle = "background-color: #C42B1C; border: 1px solid #A80000;";
toolTipText = "Fehler beim Laden des Treibers.";
break;
// hier: dll vorhanden, Treiber geladen
case BCDriver::DriverState::Loaded:
case BCDriver::DriverState::Initialized:
case BCDriver::DriverState::Opened:
// ORANGE
ledStyle = "background-color: #FF8C00; border: 1px solid #A80000;";
toolTipText = "Kein Gerät verbunden.";
break;
case BCDriver::DriverState::DeviceReady:
// FLUENT GREEN (Success)
ledStyle = "background-color: #107C10; border: 1px solid #0E600E;";
toolTipText = "Verbindung erfolgreich hergestellt.";
break;
}
// Styles anwenden (immer rund machen)
_led->setStyleSheet(ledStyle + "border-radius: 6px;");
setToolTip(toolTipText);
}
/**
* @brief minimale click event
*/
void BCDriverStateWidget::mouseReleaseEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
emit clicked();
QWidget::mouseReleaseEvent(event);
}

View File

@@ -30,51 +30,8 @@
***************************************************************************/
#ifndef BCGUIHELPERS_H
#define BCGUIHELPERS_H
#include <QPushButton>
#include <QLabel>
#include <QHBoxLayout>
#include <QMouseEvent>
#include <bcdriver.h>
/**
* @brief Einfaches Buttonwidget, um zwischen Dark- und Lightmode
* zu wechseln
*/
class BCThemeSwitchButton : public QPushButton
{
Q_OBJECT
public:
explicit BCThemeSwitchButton(QWidget *parent = nullptr);
void setDarkMode( bool isDark );
signals:
void themeChanged(bool isDark);
private slots:
void toggleMode();
private:
void updateIcon();
bool _isDarkMode{false};
};
/// -----------------------------------------------------------------------------------
/// -----------------------------------------------------------------------------------
#ifndef BCDRIVERSTATEWIDGET_H
#define BCDRIVERSTATEWIDGET_H
/**
@@ -82,6 +39,12 @@ private:
* Drivers anzuzeigen.
*/
#include <QLabel>
#include <bcdriver.h>
class BCDriverStateWidget : public QWidget
{
Q_OBJECT
@@ -110,4 +73,4 @@ protected:
};
#endif // BCGUIHELPERS_H
#endif // BCDRIVERSTATEWIDGET_H

View File

@@ -151,7 +151,6 @@ BCDriver::DriverStateResult BCDriverTinyCan::loadDriver()
return _driverState;
};
// #1. erstmal komplett zurücksetzen
resetDriver();
// #2. Treiber laden, initialisieren und
@@ -177,27 +176,18 @@ BCDriver::DriverStateResult BCDriverTinyCan::loadDriver()
BCDriver::DriverStateResult BCDriverTinyCan::setConsoleSlaveMode()
{
// Wir versuchen ein Test-Byte zu lesen, hier: einfach die Hardware
// Revision der Console.
uint32_t console = static_cast<uint32_t>(BCDevice::ID::Console);
uint8_t slaveFlag = static_cast<uint8_t> (BC::ID::Cons_Status_Slave);
qDebug() << "XXX BCDriverTinyCan::Driver Init: putting Console in slave mode ... ";
unsigned int retry = cTimeOuts;
TransmitResult isSlave = 0;
// Already slave?
isSlave = readRawByte( console, slaveFlag );
if( isSlave.has_value() )
{
qDebug() << "Console responded: " << isSlave.value();
if( isSlave.value() == 1 )
{
qDebug() << "Console already in slave mode. good!";
return DriverState::DeviceReady;
}
}
qDebug() << "BCDriverTinyCan::BCDriverTinyCan::XXX Driver Init: putting Console in slave mode ... ";
unsigned int retry = cTimeOuts;
if( isSlave.has_value() && isSlave.value() == 1 )
goto happyEnd;
do
{
@@ -208,11 +198,17 @@ BCDriver::DriverStateResult BCDriverTinyCan::setConsoleSlaveMode()
} while( retry-- && !(*isSlave) );
bc::delay_millis( 500 ); // give the Console some time to settle
//if( !isSlave )
//emit statusHint( QString("putting Console in slave mode ") + (isSlave ? "done" : "failed") );
if( isSlave.has_value() && isSlave.value() == 1 )
goto happyEnd;
// ist das jetzt irgendwie schlimm, wenn wir keine slave Console haben
return isSlave ? DriverState::DeviceReady : DriverState::Opened;
return DriverState::Opened;
happyEnd:
_driverState = DriverState::DeviceReady;
return DriverState::DeviceReady;
}
@@ -237,75 +233,14 @@ void BCDriverTinyCan::resetDriver()
TransmitResult BCDriverTinyCan::readRawByte( uint32_t deviceID, uint8_t registerID ) const
{
qDebug() << " --- CAN Read Byte: Device: "<< deviceID << " register: " << registerID << " TRY! ";
struct TCanMsg msg;
int err, retry = 20;
int timeout = 80;
unsigned char receipient = (unsigned char) deviceID;
unsigned char reg = (unsigned char) registerID;
msg.MsgFlags = 0L;
msg.Id = receipient;
msg.MsgLen = 2;
msg.MsgData[0] = 0x00;
msg.MsgData[1] = reg;
CanTransmit(0, &msg, 1);
while(timeout-- && CanTransmitGetCount(0))
bc::delay_millis( cTIMEOUT_MS );
if (timeout == -1)
qDebug() << "error: could not send value to node ";
retry:
timeout = 80;
while(timeout-- && !CanReceiveGetCount(0))
bc::delay_millis( cTIMEOUT_MS );
if (timeout == -1)
{
qDebug() << "error: no response from node";
return 0;
}
if ((err = CanReceive(0, &msg, 1)) > 0)
{
qDebug() << " retry: " << retry << " BIB:" << BC::ID::ID_Bib << " msg.Id: " << msg.Id << " msg.MsgLen: " << msg.MsgLen << " msg.MsgData[1]: " << msg.MsgData[1] << " reg: " << reg;
if (--retry && (msg.Id != (uint32_t)BC::ID::ID_Bib|| msg.MsgLen != 4 || msg.MsgData[1] != reg))
goto retry;
if (!retry)
{
qDebug() << "XXX error: no response from node: " << err;
return 0;
}
qDebug() << " --- CAN Read Byte: Device: "<< deviceID << " register: " << registerID << " BYTE: " << (uint32_t) msg.MsgData[3];
return (unsigned int) msg.MsgData[3];
}
else
{
qDebug() << "Error:" <<err;
}
return 0;
/*
//TransmitResult
qDebug() << " --- BCDriverTinyCan::readRawByte DriverState: " << getDriverState();
if( _driverState <DriverState::Opened )
return std::unexpected(QString("readRawValue error: driver not loaded." ) );
return std::unexpected(QString("readRawValue error: Treiber nicht geladen." ) );
unsigned char receipient = (unsigned char ) deviceID;
::TCanMsg msg;
// msg verpacken
msg.MsgFlags = 0L;
msg.Id = receipient;//deviceID;
msg.Id = deviceID;
msg.MsgLen = 2;
msg.MsgData[0] = 0x00;
msg.MsgData[1] = registerID;
@@ -313,16 +248,15 @@ retry:
// msg verschicken
::CanTransmit( 0, &msg, 1 );
int retries = cRetries; // 5?
// cTimeOuts (== 20) mal cTIMEOUT_MS (== 10 ms ) Versuche ...
int timeOuts = cTimeOuts; // 20 ?
int retries = cRetries; // 5
int timeOuts = cTimeOuts; // 20
// ... warten bis der Sendepuffer leer ist
while( timeOuts-- && ::CanTransmitGetCount( 0 ) )
bc::delay_millis( cTIMEOUT_MS );
if( timeOuts == -1 )
return std::unexpected(QString("readRawValue error: could not send value" ));
return std::unexpected(QString("readRawValue error: Sendefehler" ));
retry:
@@ -333,46 +267,34 @@ retry:
bc::delay_millis( cTIMEOUT_MS );
if( timeOuts == -1 )
return std::unexpected(QString("getValue error: no response from node" ));
return std::unexpected(QString("readRawValue error: (Node)Timeout" ));
// message empfangen
int err = ::CanReceive( 0, &msg, 1 );
//qDebug() << "HÄÄ ?" << err << "reg: "<< registerID <<" timeOuts: " << timeOuts;
if( err < 0 )
//throw BCException( "getValue error: could not receive value" );
qDebug( "getValue error: could not receive value" );
return std::unexpected(QString("readRawValue error: Lesefehler" ));
//qDebug() << "HÄÄ 2" <<msg.Id;
//qDebug() << "HÄÄ 2" <<msg.MsgLen;
//qDebug() << "HÄÄ 2" <<msg.MsgData[1];
//if( err > 0 )
if( --retries && ( msg.Id != BIB || msg.MsgLen != 4 || msg.MsgData[1] != registerID ) )
if( --retries && ( msg.Id != (uint32_t)BC::ID::ID_Bib || msg.MsgLen != 4 || msg.MsgData[1] != registerID ) )
goto retry;
if( !timeOuts )
return std::unexpected(QString("CAN response errror: timeout" ));
return std::unexpected(QString("CAN response errror: Timeout" ));
qDebug() << " --- CAN Read Byte: " << (uint32_t) msg.MsgData[3] << " Device:: "<< deviceID << " register: " << registerID;
return (uint32_t) msg.MsgData[3];
*/
}
// void BCDriverTinyCan::setValue( unsigned char receipient, unsigned char reg, unsigned char value )
TransmitResult BCDriverTinyCan::writeRawByte( uint32_t deviceID, uint8_t registerID, uint8_t value ) const
{
if( _driverState <DriverState::Opened )
return std::unexpected(QString("readRawValue error: driver not loaded." ) );
qDebug() << " --- BCDriverTinyCan writeRawValue: " << value;
::TCanMsg msg;
int timeout_count = cTIMEOUT_COUNT;
msg.MsgFlags = 0L;
msg.Id = deviceID;
msg.MsgLen = 4;
@@ -389,6 +311,6 @@ TransmitResult BCDriverTinyCan::writeRawByte( uint32_t deviceID, uint8_t registe
if( timeout_count == -1 )
return std::unexpected(QString("error: could not send value to %1" ).arg( deviceID ) );
return uint32_t(1); // als 'true'
return uint32_t(0); // kein Fehler
}

View File

@@ -1,250 +0,0 @@
/***************************************************************************
BionxControl
© 2025 -2026 christoph holzheuer
christoph.holzheuer@gmail.com
Using:
mhs_can_drv.c
© 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany
Klaus Demlehner, klaus@mhs-elektronik.de
@see www.mhs-elektronik.de
Based on Bionx data type descriptions from:
BigXionFlasher USB V 0.2.4 rev. 97
© 2011-2013 by Thomas Koenig <info@bigxionflasher.org>
@see www.bigxionflasher.org
Bionx Bike Info
© 2018 Thorsten Schmidt (tschmidt@ts-soft.de)
@see www.ts-soft.de
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
@see https://github.com/bikemike/bionx-bikeinfo
***************************************************************************/
#include <bcguihelpers.h>
BCThemeSwitchButton::BCThemeSwitchButton(QWidget *parent )
: QPushButton(parent)
{
// Visuelles Setup: Flach, keine Ränder, Hand-Cursor
setFlat(true);
setCursor(Qt::PointingHandCursor);
setFixedSize(24, 24); // Kleiner Footprint im StatusBar
// CSS: Transparent, damit es sich nahtlos in den StatusBar einfügt
// Schriftgröße etwas erhöhen, damit die Emojis gut erkennbar sind
setStyleSheet(R"(
BCThemeSwitchButton
{
border: none;
background-color: green;
font-size: 11pt;
}
BCThemeSwitchButton:Hover
{
background-color: rgba(128, 128, 128, 30);
border-radius: 24px;
}
)");
// Initialer Status (Startet im Dark Mode -> zeigt Mond)
updateIcon();
connect(this, &QPushButton::clicked, this, &BCThemeSwitchButton::toggleMode);
}
/**
* @brief Setzt den DarkMode
*/
void BCThemeSwitchButton::setDarkMode( bool isDark )
{
_isDarkMode = !isDark;
toggleMode();
}
/**
* @brief Schaltet den akutellen Mode um.
*/
void BCThemeSwitchButton::toggleMode()
{
_isDarkMode = !_isDarkMode;
updateIcon();
emit themeChanged(_isDarkMode);
}
/**
* @brief Icon & Tooltip anpassen
*/
void BCThemeSwitchButton::updateIcon()
{
// Logik:
// Ist Dark Mode an? Zeige Mond (oder Sonne, je nach Geschmack).
// Hier: Zeige das Symbol des AKTUELLEN Modus.
setText(_isDarkMode ? "🌙" : "☀️");
setToolTip(_isDarkMode ? "Zum LightMode wechseln" : "Zum DarkMode wechseln");
}
/// -----------------------------------------------------------------------------------
/// -----------------------------------------------------------------------------------
/**
* @brief Hilfswidget: Zeigt den DriverState als Icon an.
*/
BCDriverStateWidget::BCDriverStateWidget(QWidget* parent)
: QWidget(parent)
{
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(10, 2, 10, 2);
//layout->setSpacing(8);
_led = new QLabel(this);
_led->setFixedSize(12, 12);
layout->addWidget(_led);
// Startzustand
onDriverStateChanged(BCDriver::DriverState::NotPresent, "Not Present");
}
// Hauptfunktion zum Setzen des Status
// 'customMessage' ist optional. Wenn leer, wird ein Standardtext genommen.
void BCDriverStateWidget::onDriverStateChanged(BCDriver::DriverState state, const QString& customMessage)
{
_state = state;
qDebug() << " --- StateWidget: " << state << " - " << customMessage;
updateStyle();
}
/*
void BCDriverStateWidget::updateStyle()
{
QString ledStyle;
QString labelColor;
QString toolTipText;
switch (_state)
{
case BCDriver::DriverState::DeviceReady:
// FLUENT GREEN (Success)
ledStyle = "background-color: #107C10; border: 1px solid #0E600E;#FF5F1F; #FF8C00;<- das isses #FF6700";
labelColor = "#FFFFFF"; // Weiß (Hervorgehoben)
toolTipText = "Verbindung erfolgreich hergestellt.";
break;
case BCDriver::DriverState::Error:
// FLUENT RED (Critical)
ledStyle = "background-color: #C42B1C; border: 1px solid #A80000;";
labelColor = "#FF99A4"; // Ein helleres Rot für Text, damit es auf Dunkel lesbar ist
toolTipText = "Kritischer Fehler bei der Verbindung!";
break;
default:
// FLUENT GRAY (Neutral)
// Wir machen es dunkelgrau mit hellem Rand -> "Ausgeschaltet"-Look
ledStyle = "background-color: #3B3B3B; border: 1px solid #606060;";
labelColor = "#9E9E9E"; // Ausgegrauter Text
toolTipText = "System ist offline.";
break;
}
// Styles anwenden (immer rund machen)
_led->setStyleSheet(ledStyle + "border-radius: 6px;");
// Textfarbe setzen
_label->setStyleSheet(QString("color: %1; font-weight: %2;")
.arg(labelColor)
.arg(_state == BCDriver::DriverState::DeviceReady ? "bold" : "normal"));
setToolTip(toolTipText);
}
*/
void BCDriverStateWidget::updateStyle()
{
QString ledStyle;
QString toolTipText;
/*
NotPresent,
Error,
Loaded,
Initialized,
Opened, // bis hierher: dll vorhanden, Treiber geladen
DeviceReady
*/
switch (_state)
{
case BCDriver::DriverState::NotPresent:
// FLUENT GRAY (Neutral)
// Wir machen es dunkelgrau mit hellem Rand -> "Ausgeschaltet"-Look
ledStyle = "background-color: #3B3B3B; border: 1px solid #606060;";
toolTipText = "Treiber nicht geladen.";
break;
case BCDriver::DriverState::Error:
// FLUENT RED (Critical)
ledStyle = "background-color: #C42B1C; border: 1px solid #A80000;";
toolTipText = "Fehler beim Laden des Treibers.";
break;
// hier: dll vorhanden, Treiber geladen
case BCDriver::DriverState::Loaded:
case BCDriver::DriverState::Initialized:
case BCDriver::DriverState::Opened:
// FLUENT RED (Critical)
ledStyle = "background-color: #FF8C00; border: 1px solid #A80000;";
toolTipText = "Fehler beim Laden des Treibers.";
break;
case BCDriver::DriverState::DeviceReady:
// FLUENT GREEN (Success)
ledStyle = "background-color: #107C10; border: 1px solid #0E600E;";
toolTipText = "Verbindung erfolgreich hergestellt.";
break;
}
// Styles anwenden (immer rund machen)
_led->setStyleSheet(ledStyle + "border-radius: 6px;");
/*
// Textfarbe setzen
_setStyleSheet(QString("color: %1; font-weight: %2;")
.arg(labelColor)
.arg(_state == BCDriver::DriverState::DeviceReady ? "bold" : "normal"));
*/
setToolTip(toolTipText);
}
void BCDriverStateWidget::mouseReleaseEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
emit clicked();
}
QWidget::mouseReleaseEvent(event);
}

View File

@@ -29,14 +29,16 @@
***************************************************************************/
#include <QFile>
#include <QTimer>
#include <QMessageBox>
#include "qassert.h"
#include <bcthemeswitchbutton.h>
#include <bcdriverstatewidget.h>
#include <bcmainwindow.h>
#include <bcanimateddelegate.h>
#include <bcvaluedelegate.h>
#include <ui_bcmainwindow.h>
#include <bcguihelpers.h>
/**
* @brief Das Mainwindow erzeugen
@@ -50,8 +52,12 @@ BCMainWindow::BCMainWindow(QWidget *parent)
qRegisterMetaType<BCValue>("BCValue");
qRegisterMetaType<QList<BCValue>>("BCValueList");
setupUi(this);
#if defined(Q_OS_LINUX)
// Für Touch screen: Window FRam weglassen
//setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
#endif
setupUi(this);
// Wir schreiben den 'initMainWindow()' Aufruf mit Hilfe des
// timers in die Event-Queue, damit er erst ausgeführt wird,
@@ -81,7 +87,6 @@ BCMainWindow::~BCMainWindow()
void BCMainWindow::initMainWindow()
{
// Lambda um die buttons mit ihren Actions zu verbinden
auto configureAction = [&]( QToolButton* button, QAction* action, BCDevice::ID deviceID )
{
@@ -89,20 +94,22 @@ void BCMainWindow::initMainWindow()
button->setDefaultAction( action);
// new way: die DeviceID muss aber explizit vom Lambda eingefanden werden.
connect( action, &QAction::triggered, this, [this,deviceID]()
{
onShowDevicePanel( deviceID );
});
{
onShowDevicePanel( deviceID );
});
if( _devicePanels.contains(deviceID) )
{
BCDeviceView* currentPanel = _devicePanels[deviceID];
// ... und ihre device ID
currentPanel->setDeviceID( deviceID );
// Wenn ein Device (entspricht einem Datenmodel) fertig eingelesen wurde,
// wird es weitergereicht.
// Problem: alle Panels bekommen alle Datenmodelle angeboten.
connect( &_dataManager, &BCXmlLoader::valueListReady, currentPanel, &BCDeviceView::onValueListReady );
}
{
BCDeviceView* currentPanel = _devicePanels[deviceID];
// ... und ihre device ID
currentPanel->setDeviceID( deviceID );
// Wenn ein Device (entspricht einem Datenmodel) fertig eingelesen wurde,
// wird es weitergereicht.
// Problem: alle Panels bekommen alle Datenmodelle angeboten.
connect( &_dataManager, &BCXmlLoader::valueListReady, currentPanel, &BCDeviceView::onValueListReady );
connect( currentPanel->model(), SIGNAL(makeSimonHappy()), this, SLOT(onStartAnimation() ) );
}
};
// Wir wollen die Devices den Views zuordnen können
@@ -110,63 +117,57 @@ void BCMainWindow::initMainWindow()
_devicePanels[BCDevice::ID::Battery] = _batteryPanel;
_devicePanels[BCDevice::ID::Motor] = _motorPanel;
// Die actions an die Buttons binden
// Die actions an die Buttons binden
configureAction(_motorButton, _motorAction, BCDevice::ID::Motor );
configureAction(_consoleButton, _consoleAction, BCDevice::ID::Console );
configureAction(_batteryButton, _batteryAction, BCDevice::ID::Battery );
/*
bool m_isDarkMode = false;
QString icon = m_isDarkMode ? "☀️" : "🌙";
fitzeButton->setText(icon);
QString style = QString(
"QPushButton {"
" background-color: %1;"
" border: 1px solid %2;"
" border-radius: 6px;"
" font-size: 12pt;"
" padding: 0px;"
"}"
"QPushButton:hover {"
" background-color: %3;"
"}"
).arg(m_isDarkMode ? "#2B2B2B" : "#FFFFFF")
.arg(m_isDarkMode ? "#3F3F3F" : "#E1DFDD")
.arg(m_isDarkMode ? "#3A3A3A" : "#F9F9F9");
fitzeButton->setStyleSheet(style);
*/
initStatusBar();
// besser: model::emit dataChanged
// also: emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, ValueRole});
connect( _connectButton, &QToolButton::clicked, &_transmitter, &BCTransmitter::onToggleDriverConnection );
connect( _syncButton, &QToolButton::clicked, this, &BCMainWindow::onSyncDeviceView );
_connectButton->setDefaultAction( _connectAction);
_syncButton->setDefaultAction( _syncAction);
connect( _connectAction, &QAction::triggered, &_transmitter, &BCTransmitter::onToggleDriverConnection );
connect( _syncAction, &QAction::triggered, this, &BCMainWindow::onSyncDeviceView );
connect( _exitButton, &QToolButton::clicked, qApp, &QCoreApplication::quit );
connect( &_transmitter, &BCTransmitter::valueUpdated, this, &BCMainWindow::onValueUpdated );
connect( this, &BCMainWindow::requestValueUpdate, &_transmitter, &BCTransmitter::onUpdateValue);
connect( &_worker, &QThread::finished, &_transmitter, &QObject::deleteLater);
connect( &_transmitter, &BCTransmitter::driverStateChanged, this, &BCMainWindow::onDriverStateChanged );
connect( &_transmitter, &BCTransmitter::endOfProcessing, this, &BCMainWindow::onEndOfProcessing );
connect( this, &BCMainWindow::endOfTransmission, &_transmitter, &BCTransmitter::onEndOfTransmission );
// transmitter starten
_transmitter.moveToThread(&_worker);
_worker.start();
// die Daten des eBikes laden
_dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1);
try
{
// die Daten des eBikes laden
_dataManager.loadXmlBikeData(":/bikeinfo.xml"_L1);
}
catch( BCException& exception )
{
QMessageBox::critical( this, "Ladefehler", exception.what() );
}
// Konsolendaten als erstes anzeigen
_consoleAction->trigger();
//_batteryAction->trigger();
/*
// Dummy sync beim starten
QTimer::singleShot(1000, this, [this]()
{
onSyncDeviceView();
});
}
*/
// not least
_delightWidget = new BCDelightPMWidget(this);
}
/*
// 2. Bild für den Zustand UNCHECKED (Off) hinzufügen
@@ -180,31 +181,30 @@ connectIcon.addFile(":/icons/plug_connected.svg", QSize(), QIcon::Normal, QIc
void BCMainWindow::initStatusBar()
{
QStatusBar *statBar = statusBar();
BCDriverStateWidget* conState = new BCDriverStateWidget(this);
connect( &_transmitter, &BCTransmitter::driverStateChanged, conState, &BCDriverStateWidget::onDriverStateChanged );
connect( conState, &BCDriverStateWidget::clicked, _connectAction, &QAction::trigger );
statBar->addPermanentWidget(conState);
_statusBar->addPermanentWidget(conState);
conState->installEventFilter(this);
BCThemeSwitchButton* themeBtn = new BCThemeSwitchButton(this);
statBar->addPermanentWidget(themeBtn);
_statusBar->addPermanentWidget(themeBtn);
connect(themeBtn, &BCThemeSwitchButton::themeChanged, this, [this](bool isDark)
{
QString message = isDark ? "using DarkMode." : "using LightMode.";
onShowMessage( message );
setApplicationStyleSheet( isDark ? cDarkModeStyle : cLightModeStyle );
QString message = isDark ? "Dark Mode Activated" : "Light Mode Activated";
statusBar()->showMessage( message, 3000);
setApplicationStyleSheet( isDark ? ":claude_dark_mode.qss"_L1 : ":claude_light_mode.qss"_L1 );
});
// Wir starten im light mode
//themeBtn->setDarkMode( false );
statBar->showMessage("Ready");
onShowMessage("Ready. (Using dummy driver)");
setApplicationStyleSheet(":bionxcontrol.qss"_L1);
setApplicationStyleSheet(cLightModeStyle);
}
@@ -239,6 +239,7 @@ bool BCMainWindow::setApplicationStyleSheet( QAnyStringView path )
return false;
}
qWarning() << "Konnte Stylesheet nicht laden:" << styleFile.errorString();
//qApp->setStyleSheet(" ");
return true;
}
@@ -253,59 +254,73 @@ void BCMainWindow::setHeaderLabel( const QString& headerText)
_headerLabel->setText( " BionxControl: " + headerText );
}
void BCMainWindow::onShowMessage( const QString& message, int timeOut )
{
_statusbar->showMessage( message, timeOut );
_statusBar->showMessage( message, timeOut );
}
void BCMainWindow::autoConnect()
void BCMainWindow::onStartAnimation()
{
// __fix!
// if( !connect)
// fallBack
_delightWidget->onStartChaos();
}
void BCMainWindow::onDriverStateChanged( BCDriver::DriverState state, const QString& message )
{
qDebug() << " --- on DriverStatusChanged: " << state << ":" << message;
_statusbar->showMessage( message, 8000 );
Q_UNUSED(state)
onShowMessage( message, 8000 );
}
void BCMainWindow::onShowDevicePanel( BCDevice::ID deviceID )
{
qDebug() << " --- onShowDevicePanel:" << deviceID;
if( _devicePanels.contains( deviceID ) )
{
BCDeviceView* nxtPanel = _devicePanels[deviceID];
if( nxtPanel != _currentPanel )
{
_currentPanel = nxtPanel;
qDebug() << " --- Firz: " << _currentPanel->property( BCKeyHeaderLabel );
setHeaderLabel( _currentPanel->property( BCKeyHeaderLabel ).toString() );
_stackedWidget->setCurrentWidget( nxtPanel );
setHeaderLabel( _currentPanel->property( cBCKeyHeaderLabel ).toString() );
_stackedWidget->setCurrentWidget( _currentPanel );
if( _currentPanel->firstExpose() )
{
// Dummy sync beim starten
QTimer::singleShot(1000, this, [this]()
{
onSyncDeviceView();
});
}
// knopf auch abschalten?
}
}
}
void BCMainWindow::onConnectButtonToggled(bool checked )
{
//_dataManager.setDriverConnectionState( checked );
}
/**
* @brief SLOT, wird aufgerufen, wenn der Treiber eine frischen Wert abgeholt hat.
*/
void BCMainWindow::onValueUpdated(BCDevice::ID deviceID, int index, BCValue::State state, const QString& newValue )
void BCMainWindow::onValueUpdated(BCDevice::ID deviceID, int index, BCValue::Flags newState, uint32_t rawValue )
{
qDebug() << "Reply: from: " << deviceID << " at: " << index << "finished. Success:" << (uint8_t)state << " on:" << newValue;
if( _devicePanels.contains( deviceID ) )
{
BCDeviceView& panel = *_devicePanels[deviceID];
panel.onValueUpdated( index, state, newValue );
panel.updateValue( index, newState, rawValue );
}
}
/**
* @brief SLOT, wird aufgerufen, wenn der Treiber die Datenübertrgeung beendet hat.
*/
void BCMainWindow::onEndOfProcessing()
{
_syncButton->setEnabled( true );
onShowMessage( "Synchronization complete.");
}
/**
* @brief SLOT, der aufgerufen wird, um das akutelle Device (Battery, Motor, ... )
* zu synchronisieren, d.h. die aktuellen Werte über den CAN-Bus abzufragen.
@@ -313,27 +328,28 @@ void BCMainWindow::onValueUpdated(BCDevice::ID deviceID, int index, BCValue::Sta
void BCMainWindow::onSyncDeviceView()
{
Q_ASSERT_X(_currentPanel, "onSyncDeviceView()", "_currentpanel ist null!");
const BCValueList& currentList =_currentPanel->getValueList();
qDebug() << " ---Syncing";
// wir schalten den Sync-Button hier ab,
// wenn der Autrag bearbeitet wurde, wird der
// Button wieder eingeschaltet.
_syncButton->setEnabled( false );
const BCValueList& currentList =_currentPanel->getValueListX();
// alle einzeln? echt jetzt?
QString devName = _currentPanel->property( cBCKeyHeaderLabel ).toString();
onShowMessage( "Reading: " + devName );
for( const BCValuePtr& value : currentList )
{
qDebug() << " --- begin sync of value: " << QThread::currentThreadId() << " : " << value->label;
// wir setzen auf 'lesen'
value->state.setFlag( BCValue::State::ReadMe );
value->setFlag( BCValue::Flag::ReadMe );
// statt '_transmitter.onUpdateValue( value )' müssen wir hier
// über emit requestValueUpdate() zur Thread sysnchronisation
// entkoppeln,
emit requestValueUpdate( value);
}
emit endOfTransmission();
}

View File

@@ -39,6 +39,7 @@
#include <ui_bcmainwindow.h>
#include <bcxmlloader.h>
#include <bctransmitter.h>
#include <bcdelightpmwidget.h>
class BCDeviceView;
@@ -57,26 +58,27 @@ public slots:
//void onValueListReady( BCDevice::ID deviceID );
void onShowDevicePanel( BCDevice::ID deviceID );
void onConnectButtonToggled(bool active );
void onDriverStateChanged( BCDriver::DriverState state, const QString& message="" );
// Slots für Rückmeldungen vom Transmitter
void onValueUpdated( BCDevice::ID deviceID, int index, BCValue::State state, const QString& newValue="" );
void onValueUpdated( BCDevice::ID deviceID, int index, BCValue::Flags newState, uint32_t rawValue );
void onEndOfProcessing();
void onSyncDeviceView();
void onShowMessage( const QString& message, int timeOut=3000);
void onShowMessage( const QString& message, int timeOut=4000);
void onStartAnimation();
signals:
// Internes Signal, um Daten an den Worker Thread zu senden
void requestValueUpdate( BCValuePtrConst value);
void endOfTransmission();
protected:
bool setApplicationStyleSheet( QAnyStringView path );
void initMainWindow();
void initStatusBar();
void autoConnect();
//bool eventFilter(QObject *obj, QEvent *event) override;
@@ -86,13 +88,15 @@ protected:
// und dem Device, das sie darstellen.
using BCDeviceViews = QHash<BCDevice::ID, BCDeviceView*>;
BCDeviceViews _devicePanels;
BCDeviceView* _currentPanel{};
BCDeviceViews _devicePanels;
BCDeviceView* _currentPanel{};
BCDelightPMWidget* _delightWidget{};
QThread _worker;
BCTransmitter _transmitter;
QThread _worker;
BCTransmitter _transmitter;
static constexpr const char* BCKeyHeaderLabel = "BCHeaderLabel";
static constexpr const char* cBCKeyHeaderLabel = "BCHeaderLabel";
static constexpr const char* cDarkModeStyle = ":bc_dark.qss";
static constexpr const char* cLightModeStyle = ":bc_light.qss";
};

View File

@@ -165,14 +165,10 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="bionxcontrol.qrc">
<normaloff>:/sync_yellow.svg</normaloff>:/sync_yellow.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="autoRaise">
@@ -191,14 +187,10 @@
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="bionxcontrol.qrc">
<normaloff>:/connect.svg</normaloff>:/connect.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="autoRaise">
@@ -214,17 +206,20 @@
<height>64</height>
</size>
</property>
<property name="toolTip">
<string>Quit application.</string>
</property>
<property name="text">
<string>Quit</string>
</property>
<property name="icon">
<iconset resource="bionxcontrol.qrc">
<normaloff>:/exit_red.svg</normaloff>:/exit_red.svg</iconset>
<normaloff>:/exit_red.png</normaloff>:/exit_red.png</iconset>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="autoRaise">
@@ -345,8 +340,11 @@
</item>
</layout>
</widget>
<widget class="QStatusBar" name="_statusbar"/>
<widget class="QStatusBar" name="_statusBar"/>
<action name="_motorAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:bionx_motor.png</normaloff>:bionx_motor.png</iconset>
@@ -355,10 +353,13 @@
<string>motor</string>
</property>
<property name="toolTip">
<string>Motoreinstellungen anzeigen und bearbeiten</string>
<string>Show and edit motor settings.</string>
</property>
</action>
<action name="_batteryAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:bionx_akku.png</normaloff>:bionx_akku.png</iconset>
@@ -367,10 +368,13 @@
<string>battery</string>
</property>
<property name="toolTip">
<string>Batterieeinstellungen anzeigen und bearbeiten</string>
<string>Show and edit battery settings.</string>
</property>
</action>
<action name="_consoleAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:bionx_console.png</normaloff>:bionx_console.png</iconset>
@@ -379,19 +383,37 @@
<string>console</string>
</property>
<property name="toolTip">
<string>Konseleneinstellungen anzeigen und bearbeiten</string>
<string>Show and edit console settings.</string>
</property>
</action>
<action name="_connectAction">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:/connected.png</normaloff>:/connected.png</iconset>
<iconset resource="bionxcontrol.qrc">
<normaloff>:/connect.png</normaloff>:/connect.png</iconset>
</property>
<property name="text">
<string>connect</string>
</property>
<property name="toolTip">
<string>TinyCAN native Treiber laden</string>
<string>Load TinyCAN native driver.</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::TextHeuristicRole</enum>
</property>
</action>
<action name="_syncAction">
<property name="icon">
<iconset resource="bionxcontrol.qrc">
<normaloff>:/sync.png</normaloff>:/sync.png</iconset>
</property>
<property name="text">
<string>sync</string>
</property>
<property name="toolTip">
<string>Synchronise with eBike settings.</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::TextHeuristicRole</enum>

View File

@@ -31,4 +31,169 @@
#include <bcsliderstyle.h>
#include <bcvalueeditor.h>
BCSliderStyle::BCSliderStyle()
: QProxyStyle()
{
}
int BCSliderStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget ) const
{
switch (metric)
{
case PM_SliderThickness:
return 24; // Höhe für horizontalen Slider
case PM_SliderLength:
return 16; // Handle-Größe
case PM_SliderControlThickness:
return 16;
case PM_SliderSpaceAvailable:
if (option)
{
if (const QStyleOptionSlider* sliderOpt = qstyleoption_cast<const QStyleOptionSlider*>(option))
{
return sliderOpt->rect.width() - 20;
}
}
return QProxyStyle::pixelMetric(metric, option, widget);
default:
return QProxyStyle::pixelMetric(metric, option, widget);
}
}
QRect BCSliderStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex* opt,SubControl sc, const QWidget* widget) const
{
if (cc == CC_Slider) {
if (const QStyleOptionSlider* slider = qstyleoption_cast<const QStyleOptionSlider*>(opt))
{
QRect rect = slider->rect;
int handleSize = 16;
if (sc == SC_SliderHandle)
{
// Handle Position korrekt berechnen
if (slider->orientation == Qt::Horizontal)
{
int range = slider->maximum - slider->minimum;
int pos = slider->sliderPosition - slider->minimum;
int pixelRange = rect.width() - handleSize;
int pixelPos = (range != 0) ? (pos * pixelRange) / range : 0;
return QRect(rect.x() + pixelPos,
rect.center().y() - handleSize / 2,
handleSize, handleSize);
}
else
{
int range = slider->maximum - slider->minimum;
int pos = slider->sliderPosition - slider->minimum;
int pixelRange = rect.height() - handleSize;
int pixelPos = (range != 0) ? (pos * pixelRange) / range : 0;
return QRect(rect.center().x() - handleSize / 2,
rect.bottom() - pixelPos - handleSize,
handleSize, handleSize);
}
}
}
}
return QProxyStyle::subControlRect(cc, opt, sc, widget);
}
void BCSliderStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const
{
if (control == CC_Slider)
{
if (const QStyleOptionSlider* slider = qstyleoption_cast<const QStyleOptionSlider*>(option)) {
painter->setRenderHint(QPainter::Antialiasing);
// Fluent Colors
QColor accentColor(0, 120, 212); // #0078D4
QColor inactiveColor(138, 136, 134); // #8A8886
QColor bgColor(255, 255, 255); // White background
//QColor disabledText = option.palette.color(QPalette::Disabled, QPalette::Text);
//painter->setBrush(disabledText);
//QColor bgColor = Qt::green;//option->palette.color(QPalette::Base);
drawHorizontalFluentSlider(painter, slider, accentColor, inactiveColor, bgColor);
return;
}
}
QProxyStyle::drawComplexControl(control, option, painter, widget);
}
void BCSliderStyle::drawHorizontalFluentSlider(QPainter* painter, const QStyleOptionSlider* slider,
const QColor& activeColor, const QColor& inactiveColor,
const QColor& bgColor) const
{
QRect groove = slider->rect;
QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, nullptr);
qDebug() << " ---WTF: " << groove;
BCValueEditor::paintSliderIndicator(painter, groove, 0.5 );
/*
int grooveHeight = 4;
// Track sollte im Widget-Zentrum sein, nicht im groove-Zentrum
int grooveY = groove.center().y() - grooveHeight / 2;
// Full background track
QRect fullTrack(groove.left(), grooveY, groove.width(), grooveHeight);
painter->setPen(Qt::NoPen);
painter->setBrush(inactiveColor.lighter(150));
painter->drawRoundedRect(fullTrack, grooveHeight / 2, grooveHeight / 2);
QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, nullptr);
// Active track (filled portion)
int activeWidth = handle.center().x() - groove.left();
QRect activeTrack(groove.left(), grooveY, activeWidth, grooveHeight);
painter->setBrush(activeColor);
painter->drawRoundedRect(activeTrack, grooveHeight / 2, grooveHeight / 2);
*/
// Handle (Thumb) - Fluent style is more subtle
int handleSize = 16;
QRect thumbRect(handle.center().x() - handleSize / 2,
handle.center().y() - handleSize / 2,
handleSize, handleSize);
// Hover effect - subtle glow
if (slider->state & State_MouseOver)
{
painter->setBrush(QColor(activeColor.red(), activeColor.green(),
activeColor.blue(), 30));
int glowSize = 18;
QRect glow(handle.center().x() - glowSize / 2,
handle.center().y() - glowSize / 2,
glowSize, glowSize);
painter->drawEllipse(glow);
}
// Thumb
painter->setBrush(bgColor);
painter->setPen(QPen(activeColor, 2));
painter->drawEllipse(thumbRect);
// Inner circle for pressed state
if (slider->state & State_Sunken) {
int innerSize = 6;
QRect inner(handle.center().x() - innerSize / 2,
handle.center().y() - innerSize / 2,
innerSize, innerSize);
painter->setPen(Qt::NoPen);
painter->setBrush(activeColor);
painter->drawEllipse(inner);
}
}

View File

@@ -59,210 +59,25 @@
#include <QGraphicsDropShadowEffect>
// Fluent Design Slider Style
class FluentSliderStyle : public QProxyStyle
class BCSliderStyle : public QProxyStyle
{
public:
FluentSliderStyle()
: QProxyStyle()
{}
BCSliderStyle();
// Wichtig: Genug Platz für Handle reservieren
int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override
{
switch (metric)
{
case PM_SliderThickness:
return 32; // Höhe für horizontalen Slider
case PM_SliderLength:
return 20; // Handle-Größe
case PM_SliderControlThickness:
return 20;
case PM_SliderSpaceAvailable:
if (option)
{
if (const QStyleOptionSlider* sliderOpt = qstyleoption_cast<const QStyleOptionSlider*>(option)) {
if (sliderOpt->orientation == Qt::Horizontal) {
return sliderOpt->rect.width() - 20;
} else {
return sliderOpt->rect.height() - 20;
}
}
}
return QProxyStyle::pixelMetric(metric, option, widget);
int pixelMetric(PixelMetric metric, const QStyleOption* option = nullptr, const QWidget* widget = nullptr) const override;
default:
return QProxyStyle::pixelMetric(metric, option, widget);
}
}
QRect subControlRect(ComplexControl cc, const QStyleOptionComplex* opt, SubControl sc, const QWidget* widget) const override;
void drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const override;
void drawHorizontalFluentSlider(QPainter* painter,
const QStyleOptionSlider* slider,
const QColor& activeColor,
const QColor& inactiveColor,
const QColor& bgColor) const;
QRect subControlRect(ComplexControl cc, const QStyleOptionComplex* opt,SubControl sc, const QWidget* widget) const override
{
if (cc == CC_Slider) {
if (const QStyleOptionSlider* slider = qstyleoption_cast<const QStyleOptionSlider*>(opt)) {
QRect rect = slider->rect;
int handleSize = 20;
static void paintSliderIndicator(QPainter* painter, const QRect& rect, double ratio );
if (sc == SC_SliderHandle) {
// Handle Position korrekt berechnen
if (slider->orientation == Qt::Horizontal) {
int range = slider->maximum - slider->minimum;
int pos = slider->sliderPosition - slider->minimum;
int pixelRange = rect.width() - handleSize;
int pixelPos = (range != 0) ? (pos * pixelRange) / range : 0;
return QRect(rect.x() + pixelPos,
rect.center().y() - handleSize / 2,
handleSize, handleSize);
} else {
int range = slider->maximum - slider->minimum;
int pos = slider->sliderPosition - slider->minimum;
int pixelRange = rect.height() - handleSize;
int pixelPos = (range != 0) ? (pos * pixelRange) / range : 0;
return QRect(rect.center().x() - handleSize / 2,
rect.bottom() - pixelPos - handleSize,
handleSize, handleSize);
}
}
}
}
return QProxyStyle::subControlRect(cc, opt, sc, widget);
}
void drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const override
{
if (control == CC_Slider)
{
if (const QStyleOptionSlider* slider = qstyleoption_cast<const QStyleOptionSlider*>(option)) {
painter->setRenderHint(QPainter::Antialiasing);
// Fluent Colors
QColor accentColor(0, 120, 212); // #0078D4
QColor inactiveColor(138, 136, 134); // #8A8886
QColor bgColor(255, 255, 255); // White background
if (slider->orientation == Qt::Horizontal) {
drawHorizontalFluentSlider(painter, slider, accentColor, inactiveColor, bgColor);
} else {
drawVerticalFluentSlider(painter, slider, accentColor, inactiveColor, bgColor);
}
return;
}
}
QProxyStyle::drawComplexControl(control, option, painter, widget);
}
private:
void drawHorizontalFluentSlider(QPainter* painter, const QStyleOptionSlider* slider,
const QColor& activeColor, const QColor& inactiveColor,
const QColor& bgColor) const {
QRect groove = slider->rect;
QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, nullptr);
int grooveHeight = 4;
// Track sollte im Widget-Zentrum sein, nicht im groove-Zentrum
int grooveY = slider->rect.center().y() - grooveHeight / 2;
// Full background track
QRect fullTrack(groove.left(), grooveY, groove.width(), grooveHeight);
painter->setPen(Qt::NoPen);
painter->setBrush(inactiveColor.lighter(150));
painter->drawRoundedRect(fullTrack, grooveHeight / 2, grooveHeight / 2);
// Active track (filled portion)
int activeWidth = handle.center().x() - groove.left();
QRect activeTrack(groove.left(), grooveY, activeWidth, grooveHeight);
painter->setBrush(activeColor);
painter->drawRoundedRect(activeTrack, grooveHeight / 2, grooveHeight / 2);
// Handle (Thumb) - Fluent style is more subtle
int handleSize = 20;
QRect thumbRect(handle.center().x() - handleSize / 2,
handle.center().y() - handleSize / 2,
handleSize, handleSize);
// Hover effect - subtle glow
if (slider->state & State_MouseOver) {
painter->setBrush(QColor(activeColor.red(), activeColor.green(),
activeColor.blue(), 30));
int glowSize = 32;
QRect glow(handle.center().x() - glowSize / 2,
handle.center().y() - glowSize / 2,
glowSize, glowSize);
painter->drawEllipse(glow);
}
// Thumb
painter->setBrush(bgColor);
painter->setPen(QPen(activeColor, 2));
painter->drawEllipse(thumbRect);
// Inner circle for pressed state
if (slider->state & State_Sunken) {
int innerSize = 8;
QRect inner(handle.center().x() - innerSize / 2,
handle.center().y() - innerSize / 2,
innerSize, innerSize);
painter->setPen(Qt::NoPen);
painter->setBrush(activeColor);
painter->drawEllipse(inner);
}
}
void drawVerticalFluentSlider(QPainter* painter, const QStyleOptionSlider* slider,
const QColor& activeColor, const QColor& inactiveColor,
const QColor& bgColor) const {
QRect groove = slider->rect;
QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, nullptr);
int grooveWidth = 4;
// Track sollte im Widget-Zentrum sein
int grooveX = slider->rect.center().x() - grooveWidth / 2;
// Full background track
QRect fullTrack(grooveX, groove.top(), grooveWidth, groove.height());
painter->setPen(Qt::NoPen);
painter->setBrush(inactiveColor.lighter(150));
painter->drawRoundedRect(fullTrack, grooveWidth / 2, grooveWidth / 2);
// Active track
int activeHeight = groove.bottom() - handle.center().y();
QRect activeTrack(grooveX, handle.center().y(), grooveWidth, activeHeight);
painter->setBrush(activeColor);
painter->drawRoundedRect(activeTrack, grooveWidth / 2, grooveWidth / 2);
// Handle
int handleSize = 20;
QRect thumbRect(handle.center().x() - handleSize / 2,
handle.center().y() - handleSize / 2,
handleSize, handleSize);
if (slider->state & State_MouseOver) {
painter->setBrush(QColor(activeColor.red(), activeColor.green(),
activeColor.blue(), 30));
int glowSize = 32;
QRect glow(handle.center().x() - glowSize / 2,
handle.center().y() - glowSize / 2,
glowSize, glowSize);
painter->drawEllipse(glow);
}
painter->setBrush(bgColor);
painter->setPen(QPen(activeColor, 2));
painter->drawEllipse(thumbRect);
if (slider->state & State_Sunken) {
int innerSize = 8;
QRect inner(handle.center().x() - innerSize / 2,
handle.center().y() - innerSize / 2,
innerSize, innerSize);
painter->setPen(Qt::NoPen);
painter->setBrush(activeColor);
painter->drawEllipse(inner);
}
}
};

49
bcthemeswitchbutton.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include <bcthemeswitchbutton.h>
BCThemeSwitchButton::BCThemeSwitchButton(QWidget *parent )
: QPushButton(parent)
{
// Visuelles Setup: Flach, keine Ränder, Hand-Cursor
setFlat(true);
setCursor(Qt::PointingHandCursor);
setFixedSize(24, 24);
updateIcon();
connect(this, &QPushButton::clicked, this, &BCThemeSwitchButton::toggleMode);
}
/**
* @brief Setzt den DarkMode
*/
void BCThemeSwitchButton::setDarkMode( bool isDark )
{
_isDarkMode = !isDark;
toggleMode();
}
/**
* @brief Schaltet den akutellen Mode um.
*/
void BCThemeSwitchButton::toggleMode()
{
_isDarkMode = !_isDarkMode;
updateIcon();
emit themeChanged(_isDarkMode);
}
/**
* @brief Icon & Tooltip anpassen
*/
void BCThemeSwitchButton::updateIcon()
{
setText(_isDarkMode ? "🌙" : "☀️");
setToolTip(_isDarkMode ? "Use LightMode" : "Use DarkMode");
}

72
bcthemeswitchbutton.h Normal file
View File

@@ -0,0 +1,72 @@
/***************************************************************************
BionxControl
© 2025 -2026 christoph holzheuer
christoph.holzheuer@gmail.com
Using:
mhs_can_drv.c
© 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany
Klaus Demlehner, klaus@mhs-elektronik.de
@see www.mhs-elektronik.de
Based on Bionx data type descriptions from:
BigXionFlasher USB V 0.2.4 rev. 97
© 2011-2013 by Thomas Koenig <info@bigxionflasher.org>
@see www.bigxionflasher.org
Bionx Bike Info
© 2018 Thorsten Schmidt (tschmidt@ts-soft.de)
@see www.ts-soft.de
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
@see https://github.com/bikemike/bionx-bikeinfo
***************************************************************************/
#ifndef BCTHEMESWITCHBUTTON_H
#define BCTHEMESWITCHBUTTON_H
#include <QPushButton>
#include <bcdriver.h>
/**
* @brief Einfaches Buttonwidget, um zwischen Dark- und Lightmode
* zu wechseln
*/
class BCThemeSwitchButton : public QPushButton
{
Q_OBJECT
public:
explicit BCThemeSwitchButton(QWidget *parent = nullptr);
void setDarkMode( bool isDark );
signals:
void themeChanged(bool isDark);
private slots:
void toggleMode();
private:
void updateIcon();
bool _isDarkMode{false};
};
#endif // BCTHEMESWITCHBUTTON_H

125
bctoggleswitch.cpp Normal file
View File

@@ -0,0 +1,125 @@
#include "bctoggleswitch.h"
#include <QPainter>
#include <QPropertyAnimation>
#include <QEasingCurve>
#include <QEnterEvent>
#include <QEvent>
BCToggleSwitch::BCToggleSwitch(QWidget *parent)
: QAbstractButton(parent)
, m_position(0.0f)
{
setCheckable(true);
setCursor(Qt::PointingHandCursor);
setFixedSize(44, 22); // Standardgröße
// Animation initialisieren
m_animation = new QPropertyAnimation(this, "position", this);
m_animation->setDuration(200);
m_animation->setEasingCurve(QEasingCurve::OutQuad);
// Signal verknüpfen
connect(this, &QAbstractButton::toggled, this, [this](bool checked){
m_animation->stop();
m_animation->setStartValue(m_position);
m_animation->setEndValue(checked ? 1.0f : 0.0f);
m_animation->start();
});
}
float BCToggleSwitch::position() const
{
return m_position;
}
void BCToggleSwitch::setPosition(float pos)
{
m_position = pos;
update(); // Trigger Repaint
}
QSize BCToggleSwitch::sizeHint() const
{
return QSize(44, 22);
}
void BCToggleSwitch::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
// --- Farben ---
// Tipp: In einem echten Projekt diese Farben als const statics
// oder aus der QPalette laden.
QColor offBorderColor = QColor(0x8D8D8D);
QColor offKnobColor = QColor(0x5D5D5D);
QColor onColor = QColor(0x0078D4); // Fluent Blue
QColor white = Qt::white;
QRectF rect = this->rect();
qreal radius = rect.height() / 2.0;
// 1. Hintergrund (Track) zeichnen
p.setPen(Qt::NoPen);
if (isChecked() || m_position > 0.5f)
{
// AN-Zustand: Hintergrund gefüllt
p.setBrush(onColor);
p.drawRoundedRect(rect, radius, radius);
}
else
{
// AUS-Zustand: Nur Rahmen
p.setBrush(Qt::NoBrush);
// Hover-Status prüfen
if (underMouse())
p.setPen(QPen(offBorderColor.darker(120), 1.5));
else
p.setPen(QPen(offBorderColor, 1.5));
// Rechteck etwas verkleinern, damit der Rahmen nicht abgeschnitten wird
p.drawRoundedRect(rect.adjusted(1, 1, -1, -1), radius - 1, radius - 1);
}
// 2. Knopf (Thumb) zeichnen
qreal padding = 3.0;
qreal knobDiameter = rect.height() - (2 * padding);
// Interpolation der Position
qreal startX = padding;
qreal endX = rect.width() - knobDiameter - padding;
qreal currentX = startX + (m_position * (endX - startX));
QRectF knobRect(currentX, padding, knobDiameter, knobDiameter);
if (isChecked() || m_position > 0.5f)
{
p.setBrush(white);
}
else
{
if (underMouse())
p.setBrush(offKnobColor.darker(110));
else
p.setBrush(offKnobColor);
}
p.setPen(Qt::NoPen);
p.drawEllipse(knobRect);
}
void BCToggleSwitch::enterEvent(QEnterEvent *event)
{
update(); // Für Hover-Effekt neu zeichnen
QAbstractButton::enterEvent(event);
}
void BCToggleSwitch::leaveEvent(QEvent *event)
{
update(); // Hover entfernen
QAbstractButton::leaveEvent(event);
}

38
bctoggleswitch.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef BCTOGGLESWITCH_H
#define BCTOGGLESWITCH_H
#include <QAbstractButton>
// Vorwärtsdeklaration spart Include-Zeit
class QPropertyAnimation;
class BCToggleSwitch : public QAbstractButton
{
Q_OBJECT
// Property für die Animation (0.0 bis 1.0)
Q_PROPERTY(float position READ position WRITE setPosition)
public:
explicit BCToggleSwitch(QWidget *parent = nullptr);
// Getter & Setter für die Animations-Property
float position() const;
void setPosition(float pos);
QSize sizeHint() const override;
protected:
void paintEvent(QPaintEvent *event) override;
void enterEvent(QEnterEvent *event) override;
void leaveEvent(QEvent *event) override;
private:
float m_position; // 0.0 = Aus, 1.0 = An
QPropertyAnimation* m_animation;
};
#endif // BCTOGGLESWITCH_H

View File

@@ -56,33 +56,23 @@ BCTransmitter::BCTransmitter(QObject *parent)
void BCTransmitter::onToggleDriverConnection( bool connect )
{
qDebug() << " --- onToggleDriverConnection: " << connect;
emit driverStateChanged(BCDriver::DriverState::Initialized, "BUSY!");
bc::delay_millis(350);
// kill all pending stuff
QCoreApplication::removePostedEvents(this, QEvent::MetaCall);
// FIX! Ende der current op abwarten!
if( connect )
emit driverStateChanged(BCDriver::DriverState::NotPresent, "Native Treiber wird geladen.");
/*
// kill all pending stuff
//QCoreApplication::removePostedEvents(this, QEvent::MetaCall);
BCDriver::DriverState state = connect ? BCDriver::DriverState::DeviceReady : BCDriver::DriverState::NotPresent;
const QString& message = connect ? "Trying to connect" : " FAILED";
emit driverStateChanged(state, message);
return;
/*
// Hier sind wir noch in GUI Thread
QMutexLocker locker(&_mutex);
// weitere operation stoppen
_isBusy = true;
connect ? connectCanDriver() : disconnectCanDriver();
_isBusy = false;
*/
}
void BCTransmitter::onStartNativeDriver()
{
connect ? connectCanDriver() : disconnectCanDriver();
}
void BCTransmitter::connectCanDriver()
{
// hier gehts nur um den echten Treiber
@@ -92,38 +82,49 @@ void BCTransmitter::connectCanDriver()
if( _tinyCanDriver.getDriverState() != BCDriver::DriverState::DeviceReady )
result = _tinyCanDriver.loadAndStartDriver();
QString message("FitzeFatze!");
// hat geklappt
if( _tinyCanDriver.getDriverState() >= BCDriver::DriverState::Opened )
{
uint32_t console = static_cast<uint32_t>(BCDevice::ID::Console);
uint8_t hwRev = static_cast<uint8_t> (BC::ID::Cons_Rev_Hw);
TransmitResult hwVersion = _tinyCanDriver.readRawByte( console, hwRev);
if( hwVersion.has_value() )
QString message("Treiber geladen (nicht verbunden)");
// Der result-Wert im ERfolgsfall ist der driver state.
if(result.has_value() )
{
switch( result.value() )
{
message = " ---- HAIL to the king!";
qDebug() << message;
// swap driver
_canDriver = &_tinyCanDriver;
}
else
{
qDebug() << "Console not responding";
case BCDriver::DriverState::Opened:
message = "Treiber geladen (Device antwortet nicht)";
break;
case BCDriver::DriverState::DeviceReady:
message = "Treiber geladen und Device verbunden.";
// swap driver
_canDriver = &_tinyCanDriver;
break;
default:
break;
}
}
else
else // Fehlerfall, wir holen die Fehlermeldung
{
message = result.error();
}
emit driverStateChanged( _tinyCanDriver.getDriverState(), message );
}
/**
* @brief Native-Treiber zurücksetzen, Dummy-Treiber wieder aktivieren.
*/
void BCTransmitter::disconnectCanDriver()
{
_tinyCanDriver.resetDriver();
_canDriver = &_dummyDriver;
emit driverStateChanged( _tinyCanDriver.getDriverState(), "Disconnected" );
emit driverStateChanged( _tinyCanDriver.getDriverState(), "Disconnected, Dummy Treiber aktiviert." );
}
@@ -143,57 +144,49 @@ void BCTransmitter::onUpdateValue( BCValuePtrConst valuePtr)
// Kosmetik
const BCValue& value = *(valuePtr.get());
qDebug() << "------- DE.-.QUEUE: " << QThread::currentThreadId() << ": " << value.label;
uint32_t devID = static_cast<uint32_t>(value.deviceID());
uint8_t regID = static_cast<uint8_t> (value.registerID());
// Value ist 'under construction'
//emit valueUpdated( value.deviceID, value.indexRow, BCValue::State::Locked );
// Für den Fehlerfall: Wir senden den alten Wert einfach zurück
uint32_t newValue = value.rawValue();
BCValue::Flag newState = BCValue::Flag::Failed;
uint32_t devID = static_cast<uint32_t>(value.deviceID);
uint8_t regID = static_cast<uint8_t> (value.registerID);
QString newVisibleValue;
BCValue::State newState = BCValue::State::NoState;
if(value.state.testFlag( BCValue::State::WriteMe ) )
if(value.testFlag( BCValue::Flag::WriteMe ) )
{
}
// oder sollen wir hier beides erlauben ? readFlag & writeFlag ?
// Was kommt dann zuerst? Schreiben und lesen als verify ?
else if( value.state.testFlag( BCValue::State::ReadMe ) )
else if( value.testFlag( BCValue::Flag::ReadMe ) )
{
// wir sind hier im anderen thread! nicht einfach so reinschreiben, nur lesen
TransmitResult result = value.isWord ? readWordValue( devID, regID ) : readByteValue( devID, regID );
TransmitResult result = value.isWord() ? readWordValue( devID, regID ) : readByteValue( devID, regID );
if( result.has_value() )
{
// quark! das gehört hier nicht hin!
newVisibleValue = value.formatValues( result.value() );
newState = BCValue::State::InSync;
}
else
{
newState = BCValue::State::Failed;
newState = BCValue::Flag::InSync;
newValue = result.value();
}
}
emit valueUpdated( value.deviceID, value.indexRow, newState, newVisibleValue );
// __fix
//bc::processEventsFor(150);
bc::delay_millis(50);
emit valueUpdated( value.deviceID(), value.indexRow(), newState, newValue );
}
/**
* @brief Wenn dieser SLOT in der Event-Queue erreicht wird, dedeutet dies, das die
* Übertrgung vom Mainwindow abgeschlossen wurde. Wir schicken zur Bestätigung das
* Signal 'endOfProcessing' (Welches dann den 'Sync' - Button wieder einschaltet.
*/
void BCTransmitter::onEndOfTransmission()
{
emit endOfProcessing();
}
void BCTransmitter::onProcessValue()
{}
TransmitResult BCTransmitter::readByteValue( uint32_t deviceID, uint8_t registerID )
{
//qDebug() << " --- YES: Read ByteValue: " << registerID;
// Wir lesen nur ein Byte und gut.
return _canDriver->readRawByte( deviceID, registerID );
}
@@ -201,8 +194,6 @@ TransmitResult BCTransmitter::readByteValue( uint32_t deviceID, uint8_t register
TransmitResult BCTransmitter::readWordValue( uint32_t deviceID, uint8_t registerID )
{
//qDebug() << " --- YES: Read WordValue: " << registerID;
uint32_t result{};
// hi byte Leseversuch.
TransmitResult value = _canDriver->readRawByte( deviceID, registerID );

View File

@@ -36,7 +36,6 @@
#include <QObject>
#include <QQueue>
#include <QMutex>
#include <atomic>
#include <bcvalue.h>
#include <bcdrivertinycan.h>
@@ -64,12 +63,12 @@ public slots:
void onToggleDriverConnection( bool connect );
void onUpdateValue(BCValuePtrConst valuePtr );
void onProcessValue();
void onStartNativeDriver();
void onEndOfTransmission();
signals:
void valueUpdated(BCDevice::ID deviceID, int index, BCValue::State state, const QString& newValue="" );
void endOfProcessing();
void valueUpdated(BCDevice::ID deviceID, int index, BCValue::Flag state, uint32_t rawValue );
void driverStateChanged( BCDriver::DriverState state, const QString& message="" );
private:
@@ -80,13 +79,6 @@ private:
TransmitResult readByteValue( uint32_t deviceID, uint8_t registerID );
TransmitResult readWordValue( uint32_t deviceID, uint8_t registerID );
//using BCDataQueue = QQueue<BCValuePtrConst>;
//BCDataQueue _valueQueue;
//QMutex _mutex;
//std::atomic<bool> _isBusy{ false };
// __fix!
BCDriver* _canDriver{};
BCDriverTinyCan _tinyCanDriver{};
BCDriverDummy _dummyDriver{};

View File

@@ -31,6 +31,7 @@
#include <QMetaEnum>
#include <QTextStream>
#include <bcvalue.h>
@@ -39,28 +40,242 @@
BCValue::BCValue( BCDevice::ID deviceID_, BC::ID registerID_)
: deviceID{deviceID_}, registerID{registerID_}
: _deviceID{deviceID_}, _registerID{registerID_}
{
visibleValue = "--";
}
QString BCValue::formatValues( uint32_t value ) const
QString BCValue::formatValue() const
{
if( factor == 1 )
return QString::number( value );
if( _factor == 1 )
return QString::number( _rawValue );
double result = value * factor;
double result =_rawValue * _factor;
return QString::number(result, 'f', 2);
}
bool BCValue::isWord() const
{
return _valueFlags.testFlag(BCValue::Flag::IsWord);
}
bool BCValue::isReadOnly() const
{
return _valueFlags.testFlag(BCValue::Flag::ReadOnly);
}
bool BCValue::testFlag( BCValue::Flag flag ) const
{
return _valueFlags.testFlag( flag );
}
void BCValue::setFlag( BCValue::Flag flag, bool state) const
{
_valueFlags.setFlag( flag, state );
}
BCDevice::ID BCValue::deviceID() const noexcept
{
return _deviceID;
}
BC::ID BCValue::registerID() const noexcept
{
return _registerID;
}
uint32_t BCValue::rawValue() const noexcept
{
return _rawValue;
}
/**
* @brief Speichert einen via CAN-Bus gelesenen Wert in
* der BCValue Struktur.
*/
void BCValue::setRawValue(uint32_t newRawValue) const
{
// die per Zufallsgenerator erzeugten Werte des Dummy-Treibers
// können beliebigen Unsinn enthalten, also müssen wir sie
// auch skalieren.
if( _valueType == ValueType::Bool )
{
_rawValue = newRawValue > 0 ? 1 : 0;
return;
}
double value = newRawValue * _factor;
if( _optMin.has_value() && _optMax.has_value() )
{
double min = _optMin.value();
double max = _optMax.value();
value = (int) qBound( min,value, max);
}
_rawValue = value / _factor;
}
BCValue::ValueType BCValue::valueType() const noexcept
{
return _valueType;
}
int BCValue::indexRow() const noexcept
{
return _indexRow;
}
void BCValue::setIndexRow(int newIndexRow)
{
_indexRow = newIndexRow;
}
QString BCValue::label() const
{
return _label;
}
QString BCValue::unitLabel() const
{
return _unitLabel;
}
void BCValue::setFromDouble( double value )
{
//if( _isReadOnly)
switch(_valueType)
{
// wir betrachten plain
case ValueType::Bool :
_rawValue = value > 0 ? 1 : 0;
break;
case ValueType::Plain :
case ValueType::Number:
case ValueType::Float:
if( _optMin.has_value() && _optMax.has_value() )
{
double min = _optMin.value();
double max = _optMax.value();
value = qBound( min,value,max);
}
_rawValue = value / _factor;
default :
break;
}
}
double BCValue::calcMinMaxRatio() const
{
double ratio = 1;
if( _optMin.has_value() && _optMax.has_value() )
{
double min = _optMin.value();
double max = _optMax.value();
double range = max - min;
// Safety: Division durch Null verhindern (wenn min == max)
if (std::abs(range) < 1e-9)
return ratio;
double value = _rawValue * _factor;
// Die eigentliche Formel
ratio = ((value - min) / range);
}
return ratio;
}
bool BCValue::valuesForSlider(ValueRange& valueRange) const
{
valueRange.value = valueRange.min = valueRange.max = 0;
// min & max sind vorraussetzung für den slider
if( !_optMin.has_value() || !_optMax.has_value() )
return false;
// wir erwarten hier, das value zwischen min
// und max liegt weil wir das schon bei setRawValue
// überprüft haben.
valueRange.value = _rawValue * _factor;
valueRange.min = _optMin.value();
valueRange.max = _optMax.value();
valueRange.ratio = calcMinMaxRatio();
return true;
}
void BCValue::dumpValue() const
{
qDebug() << "DeviceID: " << deviceID << " Register: " << registerID << " state:" " << state << " << " label: " << label;
qDebug() << "visibleValue: " << visibleValue << " min: " << min << " max: " << max << " factor: " << factor << " ValueType: " << (char)valueType << " ";
qDebug() << "indexRow: " << indexRow << " isWord: " << isWord;
qDebug() << "DeviceID: " << _deviceID << " Register: " << _registerID << " state:" " << state << " << " label: " << _label;
qDebug() << "formattedValue: " << formatValue() << " min: " << _optMin << " max: " << _optMax << " factor: " << _factor << " ValueType: " << (char)_valueType << " ";
qDebug() << "indexRow: " << _indexRow << " isWord: " << isWord() << " isRO: " << isReadOnly();
qDebug();
}
/// ----
QString BCValue::toString() const
{
QString result;
QTextStream stream(&result);
stream << *this;
/*
qDebug() << "DeviceID: " << _deviceID << " Register: " << _registerID << " state:" " << state << " << " label: " << _label;
qDebug() << "formattedValue: " << formatValue() << " min: " << _optMin << " max: " << _optMax << " factor: " << _factor << " ValueType: " << (char)_valueType << " ";
qDebug() << "indexRow: " << _indexRow << " isWord: " << isWord() << " isRO: " << isReadOnly();
qDebug();
*/
return result;
}
// Generischer Operator für ALLE Q_GADGETs
inline QTextStream& operator<<(QTextStream& out, const BCValue& obj)
{
const QMetaObject* meta = &obj.staticMetaObject;
out << meta->className() << " { ";
// Iteriere über alle Properties (Reflection)
for (int i = 0; i < meta->propertyCount(); ++i) {
QMetaProperty prop = meta->property(i);
const char* propName = prop.name();
QVariant val = prop.readOnGadget(&obj);
out << propName << ": " << val.toString();
if (i < meta->propertyCount() - 1) out << ", ";
}
out << " }";
return out;
}

117
bcvalue.h
View File

@@ -6,7 +6,7 @@
Using:
mhs_can_drv.c
mhsMEMBER _canMEMBER _drv.c
© 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany
Klaus Demlehner, klaus@mhs-elektronik.de
@see www.mhs-elektronik.de
@@ -30,18 +30,18 @@
***************************************************************************/
#ifndef BCVALUE_H
#define BCVALUE_H
#ifndef BCVALUEMEMBER_H
#define BCVALUEMEMBER_H
#include <expected>
#include <QObject>
#include <QString>
#include <QList>
#include <QVariant>
#include <expected>
#include <bc.h>
/*
Werte haben verschiedene Längen (1,2 und 4 Byte) und werder auf unterschiedliche Art und Weise
ausgelesen und geschrieben (Siehe BCValueTypeWord). Sie können also Wert-Typen zugeordnet werden. Ein Werttyp
@@ -61,10 +61,13 @@ using OptDouble = std::optional<double>;
// Enthält den gelesenen Wert oder einen Fehlerstring
using TransmitResult = std::expected<uint32_t,QString>;
// Funktionsobject, um Werte aus der Transmitterschicht zu holden
//using ReadValueFunc = std::function<TransmitResult( const BCAbstractTransmitter& transmitter, uint32_t deviceID, uint8_t registerID )>;
//using ReadValueFunc = std::function<TransmitResult( const BCAbstractTransmitter& transmitter, uint32MEMBER _t deviceID, uint8MEMBER _t registerID )>;
class BCValue
{
Q_GADGET
friend class BCXmlLoader;
public:
@@ -72,61 +75,103 @@ public:
// später der Editor
enum class ValueType : uint8_t
{
Plain,
Plain, // nur lesen, sowas wie SerialNo
Bool,
Number,
Float
};
enum class State : uint8_t
enum class Flag : uint8_t
{
NoState = 0x00,
NoFlag = 0x00,
ReadMe = 0x01,
WriteMe = 0x02,
ReadOnly = 0x04,
Locked = 0x08,
Failed = 0x10,
InSync = 0x20,
OK = 0x40
OK = 0x40,
IsWord = 0x80
};
Q_DECLARE_FLAGS(States, State )
Q_DECLARE_FLAGS(Flags, Flag )
Q_FLAG(Flags)
BCValue( BCDevice::ID deviceID_, BC::ID registerID_ );
//Q_PROPERTY(Flags valueFlags MEMBER _valueFlags )
Q_PROPERTY(BCDevice::ID deviceID MEMBER _deviceID READ deviceID )
Q_PROPERTY(BC::ID registerID MEMBER _registerID READ registerID )
Q_PROPERTY(ValueType valueType MEMBER _valueType READ valueType )
Q_PROPERTY(int indexRow MEMBER _indexRow READ indexRow)
Q_PROPERTY(QString label MEMBER _label READ label )
Q_PROPERTY(uint32_t rawValue MEMBER _rawValue READ rawValue )
Q_PROPERTY(QString unitLabel MEMBER _unitLabel READ unitLabel )
Q_PROPERTY(double factor MEMBER _factor )
//QMEMBER _PROPERTY(OptDouble MEMBER _optMin)
//QMEMBER _PROPERTY(OptDouble MEMBER _optMax)
QString formatValues( uint32_t value ) const;
void dumpValue() const;
struct ValueRange
{
int value{0};
int min{0};
int max{0};
double ratio{1};
};
BCValue( BCDevice::ID deviceID, BC::ID registerID );
QString formatValue() const;
double calcMinMaxRatio() const;
void dumpValue() const;
bool isWord() const;
bool isReadOnly() const;
bool testFlag( Flag flag ) const;
void setFlag( Flag flag, bool state=true ) const;
BCDevice::ID deviceID() const noexcept;
BC::ID registerID() const noexcept;
uint32_t rawValue() const noexcept;
void setRawValue(uint32_t newRawValue) const;
void setFromDouble( double value );
ValueType valueType() const noexcept;
int indexRow() const noexcept;
void setIndexRow(int newIndexRow);
QString label() const;
QString unitLabel() const;
bool valuesForSlider( ValueRange& valueRange ) const;
QString toString() const;
protected:
mutable Flags _valueFlags{BCValue::Flag::NoFlag};
BCDevice::ID _deviceID{BCDevice::ID::Invalid};
BC::ID _registerID{BC::ID::Invalid};
ValueType _valueType{ValueType::Plain};
int _indexRow{-1};
QString _label;
mutable uint32_t _rawValue{};
QString _unitLabel;
double _factor{1};
OptDouble _optMin;
OptDouble _optMax;
mutable States state{BCValue::State::ReadOnly};
BCDevice::ID deviceID{BCDevice::ID::Invalid};
BC::ID registerID{BC::ID::Invalid};
ValueType valueType{ValueType::Plain};
int indexRow{-1};
QString label;
mutable QString visibleValue;
mutable double rawValue;
bool isWord{false};
QString unitLabel;
double factor{1};
OptDouble min;
OptDouble max;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(BCValue::States)
//Q_DECLARE_METATYPE(const BCValue&)
Q_DECLARE_OPERATORS_FOR_FLAGS(BCValue::Flags)
Q_DECLARE_METATYPE(BCValue::Flags)
using BCValuePtr = std::shared_ptr<BCValue>;
using BCValuePtrConst = std::shared_ptr<const BCValue>;
//using BCValueList = QList<BCValue>;
using BCValueList = QList<BCValuePtr>;
Q_DECLARE_METATYPE(const BCValuePtr)
Q_DECLARE_METATYPE(BCValueList)
// Generischer Operator für ALLE GADGETs
inline QTextStream& operator<<(QTextStream& out, const BCValue& obj);
#endif // BCVALUE_H

280
bcvaluedelegate.cpp Normal file
View File

@@ -0,0 +1,280 @@
/***************************************************************************
BionxControl
© 2025 -2026 christoph holzheuer
christoph.holzheuer@gmail.com
Using:
mhs_can_drv.c
© 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany
Klaus Demlehner, klaus@mhs-elektronik.de
@see www.mhs-elektronik.de
Based on Bionx data type descriptions from:
BigXionFlasher USB V 0.2.4 rev. 97
© 2011-2013 by Thomas Koenig <info@bigxionflasher.org>
@see www.bigxionflasher.org
Bionx Bike Info
© 2018 Thorsten Schmidt (tschmidt@ts-soft.de)
@see www.ts-soft.de
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
@see https://github.com/bikemike/bionx-bikeinfo
***************************************************************************/
#include <QSlider>
#include <QLabel>
#include <QHBoxLayout>
#include <QWidget>
#include <QDebug>
#include <QPainter>
#include <QTimer>
#include <QVariantAnimation>
#include <QPropertyAnimation>
#include <QPainter>
#include <bcdeviceview.h>
#include <bcvaluedelegate.h>
#include <bcvalueeditor.h>
BCValueDelegate::BCValueDelegate(const BCValueList& valueList, BCDeviceView* view)
: QStyledItemDelegate{view}, _valueList{valueList}, _view{view}
{
}
QWidget* BCValueDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const
{
Q_UNUSED(option)
Q_UNUSED(index)
const BCValue& bcValue = *(_valueList[ index.row()].get());
BCValue::ValueRange params;
bool hasData = bcValue.valuesForSlider( params );
if( !hasData )
return nullptr;
qDebug() << " --- Create Editor: " << bcValue.label() << " value: " << params.value << " min: " << params.min << " max: " << params.max << " ratio:" << bcValue.calcMinMaxRatio()*100.0 << '%';
auto* valueEditor = new BCValueEditor(parent);
valueEditor->setValueAndRange( params );
// Signal für sofortige Updates
connect(valueEditor, &BCValueEditor::valueChanged, this, [this, valueEditor]()
{
// Commit data sofort bei Änderung
emit const_cast<BCValueDelegate*>(this)->commitData(valueEditor);
});
// Signal für sofortige Updates
connect(valueEditor, &BCValueEditor::valueCommited, this, [this, valueEditor](int newValue)
{
qDebug() << " --- value set:" << newValue;
// Commit data sofort bei Änderung
//emit const_cast<BCValueDelegate*>(this)->commitData(valueEditor);
});
return valueEditor;
}
void BCValueDelegate::setEditorData(QWidget *editor, const QModelIndex& index) const
{
Q_UNUSED(editor)
Q_UNUSED(index)
// tue nix.
}
void BCValueDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const
{
if( index.column() == 1)
{
// Daten vom Editor zurück ins Model speichern (Beim Schließen)
BCValueEditor* slider = qobject_cast<BCValueEditor*>(editor);
if (slider)
{
int value = slider->value();
model->setData(index, value, Qt::EditRole);
}
return;
}
QStyledItemDelegate::setModelData(editor, model, index);
}
void BCValueDelegate::paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
// Standard-Zeichnen (Text, Hintergrund, Selection) durchführen
QStyledItemDelegate::paint(painter, option, index);
int row = index.row();
if( index.column() != 1 )
return;
if( row<0 || row >= _valueList.size() )
return;
const BCValue& bcValue = *(_valueList[ index.row()].get());
if( !bcValue.isReadOnly() )
{
if( bcValue.valueType() == BCValue::ValueType::Bool )
paintButtonIndicator(painter, option, bcValue);
else
{
BCValueEditor::paintSliderIndicator(painter, option.rect, bcValue.calcMinMaxRatio() );
}
}
if(_rowOpacities.contains(row))
paintHighlightRow(painter,option,index.row());
}
void BCValueDelegate::paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, int row) const
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
qreal opacity =_rowOpacities.value(row);
painter->setOpacity(opacity);
// Margin von 2px
const int m = 3;
QRect itemRect = option.rect.adjusted(m,m,-m,-m);
// Border (2px solid #2196F3)
// oranger rahmen
QPen borderPen( QColor(0xFF8C00), 1);
painter->setPen(borderPen);
painter->setBrush(Qt::NoBrush);
// highlight background
//QColor highlightColor = option.palette.highlight().color();
//highlightColor.setAlphaF(0.3); // 0.0 bis 1.0 (float ist oft lesbarer)
//painter->fillRect(option.rect, highlightColor);
painter->drawRoundedRect(itemRect, 2, 2);
painter->restore();
}
void BCValueDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex& index) const
{
Q_UNUSED(index)
QRect sliderRect = BCValueEditor::updateEditorRect( option.rect );
editor->setGeometry(sliderRect); // Slider nur über Progress Bar
}
void BCValueDelegate::paintButtonIndicator(QPainter* painter, const QStyleOptionViewItem& option, const BCValue& bcValue) const
{
}
/**
* @brief Startet die Animation für die übergebene Zeile
* @param row
*/
void BCValueDelegate::onHighlightRow(int row)
{
// Alte Animation für diese Zeile stoppen falls vorhanden
if (_rowAnimations.contains(row))
{
_rowAnimations[row]->stop();
_rowAnimations[row]->deleteLater();
}
// QVariantAnimation ist flexibler als QPropertyAnimation
auto* anim = new QVariantAnimation(this);
anim->setDuration(800);
anim->setStartValue(0.0);
anim->setEndValue(1.0);
// Custom Easing für Fade-in/out Effekt
anim->setEasingCurve(QEasingCurve::OutQuad);
connect(anim, &QVariantAnimation::valueChanged, this, [this, row](const QVariant& value)
{
qreal progress = value.toReal();
qreal opacity;
// Schnelles Fade-in (20%), langsames Fade-out (80%)
if (progress < 0.2) {
opacity = progress * 5.0; // 0->1 in 20%
} else {
opacity = 1.0 - ((progress - 0.2) / 0.8); // 1->0 in 80%
}
_rowOpacities[row] = opacity;
updateRow(row);
});
connect(anim, &QVariantAnimation::finished, this, [this, row, anim]()
{
_rowOpacities.remove(row);
_rowAnimations.remove(row);
updateRow(row);
anim->deleteLater();
});
_rowAnimations[row] = anim;
anim->start(QAbstractAnimation::DeleteWhenStopped);
}
/**
* @brief Sopt alle gerade laufenden Animationen
*/
void BCValueDelegate::clearAllHighlights()
{
for(auto* anim : std::as_const(_rowAnimations))
{
anim->stop();
anim->deleteLater();
}
_rowAnimations.clear();
_rowOpacities.clear();
if (_view)
{
_view->viewport()->update();
}
}
/**
* @brief Zeichnet die übegebene Zeile neu.
* @param row
*/
void BCValueDelegate::updateRow(int row)
{
if (_view && _view->model() && row >= 0)
{
QModelIndex idx = _view->model()->index(row,1);
QRect rect = _view->visualRect(idx);
if (!rect.isEmpty()) {
_view->viewport()->update(rect);
}
}
}

View File

@@ -30,8 +30,8 @@
***************************************************************************/
#ifndef BCANIMATEDDELEGATE_H
#define BCANIMATEDDELEGATE_H
#ifndef BCVALUEDELEGATE_H
#define BCVALUEDELEGATE_H
#include <QStyledItemDelegate>
@@ -39,56 +39,53 @@
class QPropertyAnimation;
class QVariantAnimation;
class QTableView;
class BCDeviceView;
class BCAnimatedDelegate : public QStyledItemDelegate
class BCValueDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit BCAnimatedDelegate(const BCValueList& valueList, QTableView* view );
// QString displayText(const QVariant& dataValue, const QLocale& locale) const override;
explicit BCValueDelegate(const BCValueList& valueList, BCDeviceView* view );
// Zuständig für den Edit-Modus (Doppelklick)
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const override;
void setEditorData(QWidget *editor, const QModelIndex& index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex& index) const override;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex& index) const override;
void clearAllHighlights();
signals:
public slots:
void onHighlightRow(int row);
signals:
//void viewUpdateNeeded();
private:
protected:
void updateRow(int row);
void paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void paintSliderIndicator(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void paintHighlightRow(QPainter* painter, const QStyleOptionViewItem& option, int row) const;
void paintSliderIndicator(QPainter* painter, const QRect &rect, double ratio) const;
void paintButtonIndicator(QPainter* painter, const QStyleOptionViewItem& option, const BCValue& bcValue) const;
// Das ist ein Quickhack, der Delegate sollte
// nichts über die Originaldaten wissen. Die
// Datenbeschaffung ist alleine Sache des Models.
const BCValueList& _valueList;
QTableView* _view{};
BCDeviceView* _view{};
QPropertyAnimation* _animation{};
private:
QHash<int, qreal> _rowOpacities;
QHash<int, QVariantAnimation*> _rowAnimations;
QHash<int, qreal> m_rowOpacities;
QHash<int, QVariantAnimation*> m_rowAnimations;
};
#endif // BCANIMATEDDELEGATE_H
#endif // BCVALUEDELEGATE_H

97
bcvalueeditor.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include <bcsliderstyle.h>
#include <bcvalueeditor.h>
BCValueEditor::BCValueEditor( QWidget *parent )
: QWidget(parent)
{
setupUi(this);
// wir wollen ja modern sein
_slider->setStyle(new BCSliderStyle());
setAutoFillBackground(true);
QSizePolicy sp = _commitButton->sizePolicy();
sp.setRetainSizeWhenHidden(true); // <--- Das ist der magische Schalter
_commitButton->setSizePolicy(sp);
// Wenn Slider bewegt wird -> Signal nach außen senden
connect(_slider, &QSlider::valueChanged, this, [this](int val)
{
emit valueChanged(val);
});
// Wenn Reset gedrückt wird -> Slider auf 0 (löst auch valueChanged aus)
connect(_commitButton, &QPushButton::clicked, this, [this]()
{
emit valueCommited( value() );
});
}
int BCValueEditor::value() const
{
return _slider->value();
}
void BCValueEditor::setValueAndRange( const BCValue::ValueRange& params )
{
_slider->setRange( params.min, params.max);
// Block Signals verhindern Endlosschleifen, falls das Model
// das Widget während des Updates neu setzt (passiert manchmal bei Live-Updates).
if (params.value != _slider->value())
{
bool blocked = _slider->blockSignals(true);
_slider->setValue(params.value);
_slider->blockSignals(blocked);
}
}
/**
* @brief Zeichnet eine passiven Slider, um den möglichen Wertebereich des übergebenen BCValue anzuzeigen.
*/
void BCValueEditor::paintSliderIndicator(QPainter* painter, const QRect& rect, double ratio )
{
// Kleinen Slider-Indikator zeichnen
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
qDebug() << " ---WTF in paint: " <<rect;
int adjX = rect.width() - cTextBlockOffset;
// Mini Progress Bar: der Gesamtbereich
QRect barRect = rect.adjusted( adjX, 12, -10-24, -12 );
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(0xE0E0E0));
painter->drawRoundedRect(barRect, 2, 2);
// Mini Progress Bar: der Wertebereich
barRect.setWidth( ratio * barRect.width() );
painter->setBrush(QColor(0x0078D4));
painter->drawRoundedRect(barRect, 2, 2);
if(rect.x() != 0 )
painter->setBrush(Qt::blue);
else
painter->setBrush(Qt::red);
painter->drawRoundedRect(rect, 2, 2);
painter->restore();
}
QRect BCValueEditor::updateEditorRect( const QRect& rect)
{
return rect.adjusted(
rect.width() - cTextBlockOffset, // Von rechts: cTextBlockOffset (==130) px (Breite der Progress Bar)
0, // Oben: kein Offset
-cPaddingRight, // Rechts: 8px Padding
0 // Unten: kein Offset
);
}

43
bcvalueeditor.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef BCValueEditor_H
#define BCValueEditor_H
#include <QWidget>
#include <bcvalue.h>
#include <ui_bcvalueeditor.h>
class QSlider;
class QPushButton;
class BCValue;
class BCValueEditor : public QWidget, private Ui::BCValueEditor
{
Q_OBJECT
public:
explicit BCValueEditor(QWidget *parent = nullptr);
int value() const;
void setValueAndRange( const BCValue::ValueRange& params );
// helper functions
static QRect updateEditorRect( const QRect& rect);
static void paintSliderIndicator(QPainter* painter, const QRect& rect, double ratio );
signals:
void valueChanged(int value);
void valueCommited(int value);
protected:
static constexpr int cTextBlockOffset = 130;
static constexpr int cPaddingRight = 8;
static constexpr int cSliderWidth = 117;
};
#endif // BCValueEditor_H

95
bcvalueeditor.ui Normal file
View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BCValueEditor</class>
<widget class="QWidget" name="BCValueEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>111</width>
<height>24</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>24</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetMinimumSize</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSlider" name="_slider">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="_commitButton">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777212</width>
<height>16777215</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="bionxcontrol.qrc">
<normaloff>:/update.png</normaloff>:/update.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="popupMode">
<enum>QToolButton::ToolButtonPopupMode::InstantPopup</enum>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonStyle::ToolButtonIconOnly</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="bionxcontrol.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -45,8 +45,6 @@ BCValueModel::BCValueModel(QObject *parent)
}
/**
* @brief Gibt die interne Werteliste als const ref zurück
* @return Die WerteListe
@@ -81,27 +79,21 @@ void BCValueModel::takeValueList(BCValueList& newValueList)
* @param newValue Der neue sichtbare Zahlenwert, formatiert als QString, optionall
*/
void BCValueModel::onValueUpdated( int row, BCValue::State state, const QString& newVisisbleValue )
void BCValueModel::updateValue(int row, BCValue::Flags newState, uint32_t rawValue )
{
if( row > -1 && row < _valueList.size() )
{
const BCValue& value = *(_valueList[row].get());
BCValue::Flags newFlags1 = BCValue::Flag::NoFlag;
BCValue::Flags newFlags2 = newState;
// Obacht hier!
//value.valueFlags = state;
value.setRawValue( rawValue );
QModelIndex idx = index(row,1);
//qDebug();
//qDebug() << " --- OLD:"<< newVisisbleValue;
//value.dumpValue();
value.state = state;
if( !newVisisbleValue.isEmpty() && newVisisbleValue != value.visibleValue )
{
value.visibleValue = newVisisbleValue;
}
//qDebug() << " --- NEW: " << newVisisbleValue;
//value.dumpValue();
// wir schicken auf jeden fall einen update request
emit dataChanged(idx, idx, {Qt::DisplayRole, Qt::EditRole});
}
@@ -132,12 +124,10 @@ int BCValueModel::columnCount(const QModelIndex& parent) const
{
if (parent.isValid())
return 0;
return 3;
return 2;
}
/**
* @brief Gibt die Model-Daten zurück.
*/
@@ -156,66 +146,58 @@ QVariant BCValueModel::data(const QModelIndex& index, int role) const
const BCValue& value = *(_valueList.at( row ).get());
if( col == 0 )
return value.label;
return value.label();
if( col == 1)
{
if( role == Qt::DisplayRole )
return QString("%1 %2").arg( value.visibleValue, value.unitLabel);
return QString("%1 %2").arg( value.formatValue(), value.unitLabel());
return value.visibleValue;
return value.formatValue();
}
return QVariant();
/*
Das Model Gibt hier, unabhängig von der DataRole, immer das
* gesamte BCValue Object zurück. Die Umsetzung von Status- und Typeinfromationen in GUI-Elemente
* findet nicht hier, sondern im BCDelagate statt.
// falsch! wie geben hier doch ordentlich die einzelwerte zurück
// Bonus: Rechtsbündig für Zahlen
if (role == Qt::TextAlignmentRole && (index.column() == 0 || index.column() == 2)) {
return Qt::AlignRight | Qt::AlignVCenter;
}
//return QVariant::fromValue( entry );
return QVariant();
*/
}
Qt::ItemFlags BCValueModel::flags(const QModelIndex& index) const
{
Qt::ItemFlags flag = Qt::NoItemFlags|Qt::ItemNeverHasChildren;
// die label spalte lässt sich nicht editieren
if (!index.isValid() || index.column() == 0 )
return Qt::NoItemFlags;
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
return flag;
int row = index.row();
if( row>-1 && row<_valueList.size() )
{
const BCValue& bcValue = *_valueList[row].get();
flag = bcValue.isReadOnly() ? flag : flag|Qt::ItemIsEditable|Qt::ItemIsEnabled;
}
return flag;
}
bool BCValueModel::setData(const QModelIndex& index, const QVariant& value, int role)
bool BCValueModel::setData(const QModelIndex& index, const QVariant& variant, int role)
{
// __fix!
if (index.isValid() && role == Qt::EditRole)
{
BCValuePtr item = _valueList[index.row()];
qDebug() << " --- SetData: at: " << index.row() << " --- set: " << variant.toString();
BCValuePtr value = _valueList[index.row()];
// Wir erwarten hier nur den Value-Teil (vom Slider/Editor)
// Checken ob Int oder Double
if (value.canConvert<double>())
if (variant.canConvert<int>())
{
item->visibleValue = value.toString();
if( variant.toInt() == 42)
{
//emit makeSimonHappy();
}
// QUARK!
value->setRawValue( variant.toInt() );
}
_valueList[index.row()] = item;
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
return true;
}

View File

@@ -59,16 +59,16 @@ public:
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
//QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
// Nötig für Editierbarkeit
Qt::ItemFlags flags(const QModelIndex& index) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
public slots:
void updateValue(int row, BCValue::Flags newState, uint32_t rawValue );
void onValueUpdated(int index, BCValue::State state, const QString& newVisisbleValue="" );
signals:
void makeSimonHappy();
protected:

View File

@@ -51,33 +51,30 @@ BCXmlLoader::BCXmlLoader(QObject *parent)
void BCXmlLoader::loadXmlBikeData( const QString& fileName )
{
/*
auto printAttrs = [](const QXmlStreamReader& xml)
{
QStringList parts;
for (const auto &attr : xml.attributes()) {
for (const auto &attr : xml.attributes())
{
parts << attr.name().toString() + "=\"" + attr.value().toString() + "\"";
}
qDebug().noquote() << parts.join(" ");
};
*/
QMetaEnum bcDeviceEnum{QMetaEnum::fromType<BCDevice::ID>()};
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
// __fix throw
QMessageBox::warning(nullptr, "Fehler", "Datei konnte nicht geöffnet werden.");
return;
}
throw BCException( "Fehler", "Datei konnte nicht geöffnet werden.");
_xml.setDevice(&file);
if (_xml.readNextStartElement())
{
if (_xml.name() != "Bike"_L1 )
// fix throw
_xml.raiseError(QObject::tr("The file is not an 'Bike' file."));
throw BCException( "Fehler", "Falsches Datenformat.");
}
// ??
Q_ASSERT(_xml.isStartElement() && _xml.name() == "Bike"_L1);
@@ -88,25 +85,26 @@ void BCXmlLoader::loadXmlBikeData( const QString& fileName )
if (token == QXmlStreamReader::StartElement)
{
QString deviceType = _xml.attributes().value("Type"_L1).toString();
//printAttrs (_xml);
// Wir wollen die Device-ID aus dem XML Tag ermitteln
const char* deviceKey = _xml.attributes().value("Type"_L1).toLatin1().constData();
bool ok;
if( deviceType.isEmpty() )
continue;
QByteArray byteArray = deviceType.toUtf8();
const char* deviceKey = byteArray.constData();
bool ok=false;
auto optDeviceID = bcDeviceEnum.keyToValue(deviceKey,&ok);
//_currentDeviceID = BCDevice::ID( deviceID.value_or( BCDevice::ID::Invalid ) );
//if( optDeviceID.has_value())
if(ok)
{
qDebug() << " --- Device: " << _xml.name() << ": " << deviceType << " : " << optDeviceID;
//BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID.value() );
BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID );
loadXmlBikeDeviceData(currentDeviceID);
}
}
}
if(!ok)
throw BCException( "Fehler", QString("Devicetype %1 existiert nicht.").arg(deviceType) );
//BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID.value() );
BCDevice::ID currentDeviceID = BCDevice::ID( optDeviceID );
loadXmlBikeDeviceData(currentDeviceID);
} // if startElement
} // end while
}
/**
* @brief Lädt deie Daten des BionX eBikes
* @param deviceID
@@ -117,15 +115,13 @@ void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID)
auto printAttrs = [](const QXmlStreamReader& xml)
{
QStringList parts;
for (const auto &attr : xml.attributes()) {
for (const auto &attr : xml.attributes())
parts << attr.name().toString() + "=\"" + attr.value().toString() + "\"";
}
qDebug().noquote() << parts.join(" ");
};
printAttrs (_xml);
Q_ASSERT(_xml.isStartElement() && _xml.name() == "Device"_L1);
Q_ASSERT(_xml.isStartElement() && _xml.name() == BCTags::Device );
// temporäre Wertliste für neues Device
BCValueList currentValues;
@@ -133,9 +129,7 @@ void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID)
while( _xml.readNextStartElement() )
{
if( _xml.attributes().hasAttribute(BCTags::ID) )
{
//qDebug() << " --- found: " << _xml.name() << " : " << _xml.attributes().value(BCTags::ID);
{
// füllen des Parameter packs
BCValueParams params
{
@@ -146,6 +140,7 @@ void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID)
.Min = _xml.attributes().value(BCTags::Min).toString(),
.Max = _xml.attributes().value(BCTags::Max).toString(),
.IsWord = _xml.attributes().value(BCTags::IsWord).toString(),
.ReadOnly = _xml.attributes().value(BCTags::ReadOnly).toString(),
.ValueType = _xml.attributes().value(BCTags::ValueType).toString(),
};
@@ -154,7 +149,7 @@ void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID)
if(newValue)
{
// wir merken uns gleich den index in der Werteliste
(*newValue)->indexRow = currentValues.size();
(*newValue)->setIndexRow( currentValues.size() );
currentValues.push_back( *newValue );
}
@@ -169,6 +164,11 @@ void BCXmlLoader::loadXmlBikeDeviceData(BCDevice::ID deviceID)
}
/**
* @brief Erzeugt einen BCValue für die DeviceID aus dem gegebenen parameter pack
*/
std::optional<BCValuePtr> BCXmlLoader::makeValue( BCDevice::ID deviceID, const BCValueParams& params )
{
@@ -180,66 +180,57 @@ std::optional<BCValuePtr> BCXmlLoader::makeValue( BCDevice::ID deviceID, const B
{ "Float", BCValue::ValueType::Float }
};
auto setIfExists = [&]<typename T>( QStringView source, T& target )
auto setIfExists = [&]<typename T>( T& target, QStringView source )
{
if( !source.isEmpty() )
{
bool ok;
double testVal = source.toDouble(&ok);
if (ok)
target = testVal;
}
{
bool ok;
double testVal = source.toDouble(&ok);
if (ok)
target = testVal;
}
};
/*
Wir brauchen:
- einen gültige ID String um die enum ID herauszufinden.
- einen gültige UnitType String um den ValueType herauszufinden.
*/
// Wir brauchen:
// - einen gültige ID String um die enum ID herauszufinden.
// - einen gültige UnitType String um den ValueType herauszufinden.
// geht nicht auf qt6.8 von debian trixie
//std::optional<quint64> IDVal = s_bcValueEnum.keyToValue64( params.ID.toLatin1().constData() );
bool ok;
static QMetaEnum s_bcValueEnum{QMetaEnum::fromType<BC::ID>()};
int IDVal = s_bcValueEnum.keyToValue( params.ID.toLatin1().constData(), &ok );
qDebug() << " --- should create: " << params.Label;
QByteArray byteArray = params.ID.toUtf8();
int IDVal = s_bcValueEnum.keyToValue( params.ID.toUtf8().constData(), &ok );
//if( IDVal.has_value() )
if( ok )
{
BCValuePtr newValuePtr = std::make_shared<BCValue>( deviceID, static_cast<BC::ID>(IDVal) );
BCValue& newValue = *newValuePtr.get();
if( !ok )
throw BCException( "Fehler", QString("Devicetype %1 existiert nicht.").arg(params.ID) );
setIfExists( params.Factor, newValue.factor );
setIfExists( params.Min, newValue.min );
setIfExists( params.Max, newValue.max );
setIfExists( params.IsWord, newValue.isWord );
BCValuePtr newValuePtr = std::make_shared<BCValue>( deviceID, static_cast<BC::ID>(IDVal) );
BCValue& newValue = *newValuePtr.get();
newValue.label = params.Label;
newValue.unitLabel = params.UnitLabel;
// ValueType
if( !s_valueTypes.contains( params.ValueType ) )
throw BCException( "Fehler", QString("ValueType %1 existiert nicht.").arg(params.ValueType) );
if( s_valueTypes.contains( params.ValueType ) )
newValue.valueType = s_valueTypes[params.ValueType];
newValue._valueType = s_valueTypes[params.ValueType];
/*
QString ID;
QString Label;
QString UnitLabel;
QString Factor;
QString Min;
QString Max;
QString IsWord;
QString ValueType;
*/
newValue._label = params.Label;
newValue._unitLabel = params.UnitLabel;
qDebug() << " --- created: " << params.Label;
newValue.dumpValue();
setIfExists( newValue._factor, params.Factor );
setIfExists( newValue._optMin, params.Min );
setIfExists( newValue._optMax, params.Max );
return std::optional<BCValuePtr>( newValuePtr );
}
if( params.IsWord == "true")
newValue._valueFlags.setFlag( BCValue::Flag::IsWord, true );
if( params.ReadOnly == "true")
newValue._valueFlags.setFlag( BCValue::Flag::ReadOnly, true );
//newValue.dumpValue();
return std::optional<BCValuePtr>( newValuePtr );
return std::nullopt;
}

View File

@@ -66,6 +66,7 @@ protected:
QString Min;
QString Max;
QString IsWord;
QString ReadOnly;
QString ValueType;
};

View File

@@ -1,18 +1,34 @@
<RCC>
<qresource prefix="/">
<file alias="bikeinfo.xml">resources/bikeinfo.xml</file>
<file alias="bionxcontrol.qss">resources/bionxcontrol.qss</file>
<file alias="claude_dark_mode.qss">resources/claude_dark_mode.qss</file>
<file alias="claude_light_mode.qss">resources/claude_light_mode.qss</file>
<file alias="bc_light.qss">resources/bc_light.qss</file>
<file alias="bionx_akku.png">resources/bionx_akku.png</file>
<file alias="bionx_console.png">resources/bionx_console.png</file>
<file alias="bionx_motor.png">resources/bionx_motor.png</file>
<file alias="connect.svg">resources/connect.svg</file>
<file alias="exit.svg">resources/exit.svg</file>
<file alias="exit_red.svg">resources/exit_red.svg</file>
<file alias="sync_green.svg">resources/sync_green.svg</file>
<file alias="sync_yellow.svg">resources/sync_yellow.svg</file>
<file alias="connect_white.svg">resources/connect_white.svg</file>
<file alias="sync.svg">resources/sync.svg</file>
<file alias="connect.png">resources/connect.png</file>
<file alias="exit.png">resources/exit.png</file>
<file alias="exit_red.png">resources/exit_red.png</file>
<file alias="sync_green.png">resources/sync_green.png</file>
<file alias="sync_yellow.png">resources/sync_yellow.png</file>
<file alias="sync.png">resources/sync.png</file>
<file alias="bc_dark.qss">resources/bc_dark.qss</file>
<file>resources/smile/face-angel.png</file>
<file>resources/smile/face-angry.png</file>
<file>resources/smile/face-cool.png</file>
<file>resources/smile/face-crying.png</file>
<file>resources/smile/face-embarrassed.png</file>
<file>resources/smile/face-glasses.png</file>
<file>resources/smile/face-kiss.png</file>
<file>resources/smile/face-laugh.png</file>
<file>resources/smile/face-monkey.png</file>
<file>resources/smile/face-plain.png</file>
<file>resources/smile/face-raspberry.png</file>
<file>resources/smile/face-sad.png</file>
<file>resources/smile/face-sick.png</file>
<file>resources/smile/face-smile.png</file>
<file>resources/smile/face-smile-big.png</file>
<file>resources/smile/face-smirk.png</file>
<file>resources/smile/face-surprise.png</file>
<file alias="update.png">resources/update.png</file>
</qresource>
</RCC>

View File

@@ -75,20 +75,3 @@ https://github.com/MHS-Elektronik/OBD-Display
sudo apt install fonts-open-sans
RANT
--- STRUKTUR
Denglish -> Comments, Bezeichnungen 'aligned', 'given'
Weltsprache, mag ja sein,
Schlimmer: Doku & Bücher -> c++, qt, OO nicht verstanden
includes_ pfade hartcodiert statt im Makefile -> Kunstfehler
Projecte willkürlich verstreut
Respektlose Schlampigkeit: copy & paste: es werden sogar die Artefakte des MKS mitkopiert ->
---
---

View File

@@ -47,25 +47,7 @@ int main(int argc, char *argv[])
{
QApplication app(argc, argv);
/*
app.setStyleSheet(R"(
QWidget {
background-color: #F3F3F3;
font-family: 'Segoe UI Variable', 'Segoe UI', sans-serif;
}
)");
*/
/*
app.setStyleSheet(R"(
* {
font-family: 'Calibri', 'Carlito', 'Arial', sans-serif;
font-size: 12pt;
})");
*/
//QFont font("segoe UI", 12); // Name, Größe
//QFont font("calibri", 12); // Name, Größe
//app.setFont(font);
BCMainWindow w;
w.show();

230
resources/bc_dark.qss Normal file
View File

@@ -0,0 +1,230 @@
/* ===== Fluent Dark Mode Theme ===== */
/* Basierend auf Microsoft Fluent Design System */
/* === Color Palette === */
/* Background: #202020, #2b2b2b, #323232 */
/* Accent: #0078d4 (Fluent Blue) */
/* Text: #ffffff, #e4e4e4 */
/* Borders: #3d3d3d, #4d4d4d */
/* === QWidget Base === */
QWidget
{
background-color: #202020;
color: #ffffff;
font-family: "Segoe UI", "Roboto", sans-serif;
font-size: 14px;
}
QLabel#_headerLabel
{
font-size: 18pt;
/*font-weight: bold;*/
}
QWidget:disabled
{
color: #6d6d6d;
}
/* === QToolButton === */
QToolButton
{
background-color: transparent;
color: #ffffff;
border: 1px solid transparent;
border-radius: 4px;
padding: 6px 12px;
margin: 2px;
}
QToolButton:hover
{
background-color: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.08);
}
QToolButton:pressed
{
background-color: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.05);
}
QToolButton:checked
{
background-color: rgba(0, 120, 212, 0.15);
border: 1px solid #0078d4;
color: #0078d4;
}
QToolButton:checked:hover
{
background-color: rgba(0, 120, 212, 0.25);
}
QToolButton:disabled
{
color: #6d6d6d;
background-color: transparent;
}
/* Basis-Zustand: Alles weg */
QToolButton#_commitButton
{
background: transparent;
background-color: transparent;
border: none;
outline: none; /* Entfernt den Fokus-Rahmen (gepunktete Linie) */
padding: 0px; /* Entfernt Innenabstand */
margin: 0px;
}
QToolButton#_commitButton:hover
{
background-color: rgba(0, 0, 0, 0.03);
border: none;
}
QToolButton#_commitButton:pressed
{
background-color: rgba(0, 0, 0, 0.06);
border: none;
}
QToolButton#_commitButton:checked
{
background-color: transparent;
border: none;
}
QToolButton#_commitButton:focus
{
border: none;
outline: none;
}
/* === QTableView === */
QTableView
{
background-color: #2b2b2b;
color: #ffffff;
border: 1px solid #3d3d3d;
border-radius: 4px;
selection-background-color: #0078d4;
selection-color: #ffffff;
outline: 0;
}
QTableView::item
{
padding: 0px;
border: none;
outline: 0;
}
/*
QTableView::item:focus
{
outline: 0;
background-color: blue;
}
*/
QTableView::item:hover
{
background-color: rgba(255, 255, 255, 0.06);
}
QTableView::item:selected
{
border: none;
}
/* === Corner Widget (zwischen Scrollbars) === */
QTableView QTableCornerButton::section
{
background-color: #323232;
border: none;
border-right: 1px solid #3d3d3d;
border-bottom: 1px solid #3d3d3d;
}
/* === QScrollBar Vertical === */
QScrollBar:vertical
{
background: transparent;
width: 12px;
margin: 0;
}
QScrollBar::handle:vertical {
background: rgba(255, 255, 255, 0.2);
min-height: 30px;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:vertical:hover {
background: rgba(255, 255, 255, 0.35);
}
QScrollBar::handle:vertical:pressed {
background: rgba(255, 255, 255, 0.5);
}
QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical {
height: 0px;
}
QScrollBar::add-page:vertical,
QScrollBar::sub-page:vertical {
background: transparent;
}
/* === QScrollBar Horizontal === */
QScrollBar:horizontal {
background: transparent;
height: 12px;
margin: 0;
}
QScrollBar::handle:horizontal {
background: rgba(255, 255, 255, 0.2);
min-width: 30px;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:horizontal:hover {
background: rgba(255, 255, 255, 0.35);
}
QScrollBar::handle:horizontal:pressed {
background: rgba(255, 255, 255, 0.5);
}
QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal {
width: 0px;
}
QScrollBar::add-page:horizontal,
QScrollBar::sub-page:horizontal {
background: transparent;
}
/* === QToolTip === */
QToolTip
{
background-color: #323232;
color: #ffffff;
border: 1px solid #4d4d4d;
border-radius: 4px;
padding: 6px 8px;
font-size: 13px;
}

252
resources/bc_light.qss Normal file
View File

@@ -0,0 +1,252 @@
/* ===== Fluent Light Mode Theme ===== */
/* Basierend auf Microsoft Fluent Design System */
/* === Color Palette === */
/* Background: #f3f3f3, #ffffff, #fafafa */
/* Accent: #0078d4 (Fluent Blue) */
/* Text: #000000, #1f1f1f */
/* Borders: #e1e1e1, #d1d1d1 */
/* === QWidget Base === */
QWidget {
background-color: #f3f3f3;
color: #1f1f1f;
font-family: "Segoe UI", "Roboto", sans-serif;
font-size: 14px;
}
QLabel#_headerLabel
{
font-size: 18pt;
/*font-weight: bold;*/
}
QWidget:disabled {
color: #a0a0a0;
}
/* === QToolButton === */
QToolButton {
background-color: transparent;
color: #1f1f1f;
border: 1px solid transparent;
border-radius: 4px;
padding: 6px 12px;
margin: 2px;
}
QToolButton:hover {
background-color: rgba(0, 0, 0, 0.03);
border: 1px solid rgba(0, 0, 0, 0.05);
}
QToolButton:pressed {
background-color: rgba(0, 0, 0, 0.06);
border: 1px solid rgba(0, 0, 0, 0.08);
}
QToolButton:checked {
background-color: rgba(0, 120, 212, 0.1);
border: 1px solid #0078d4;
color: #0078d4;
}
QToolButton:checked:hover {
background-color: rgba(0, 120, 212, 0.15);
}
QToolButton:disabled {
color: #a0a0a0;
background-color: transparent;
}
/* Basis-Zustand: Alles weg */
QToolButton#_commitButton {
background: transparent;
background-color: transparent;
border: none;
outline: none; /* Entfernt den Fokus-Rahmen (gepunktete Linie) */
padding: 0px; /* Entfernt Innenabstand */
margin: 0px;
}
/* WICHTIG: Auch die interaktiven Zustände überschreiben,
sonst flackert der Standard-Style beim Klicken wieder auf */
QToolButton#_commitButton:hover
{
background-color: rgba(0, 0, 0, 0.03);
border: none;
}
QToolButton#_commitButton:pressed
{
background-color: rgba(0, 0, 0, 0.06);
border: none;
}
QToolButton#_commitButton:checked
{
background-color: transparent;
border: none;
}
QToolButton#_commitButton:focus {
border: none;
outline: none;
}
/* === QTableView === */
QTableView
{
background-color: #ffffff;
alternate-background-color: #fafafa;
color: #1f1f1f;
gridline-color: #e1e1e1;
border: 1px solid #d1d1d1;
border-radius: 4px;
selection-background-color: #0078d4;
selection-color: #ffffff;
}
QTableView::item
{
border: none;
}
QTableView::item:hover {
background-color: rgba(0, 0, 0, 0.03);
}
QTableView::item:selected {
background-color: #0078d4;
color: #ffffff;
}
QTableView::item:selected:hover
{
background-color: #005a9e;
}
QTableView::item:selected:!active
{
background-color: rgba(0, 120, 212, 0.3);
color: #1f1f1f;
}
QTableView:focus
{
outline: none;
}
/* === QScrollBar Vertical === */
QScrollBar:vertical {
background: transparent;
width: 12px;
margin: 0;
}
QScrollBar::handle:vertical {
background: rgba(0, 0, 0, 0.25);
min-height: 30px;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:vertical:hover {
background: rgba(0, 0, 0, 0.4);
}
QScrollBar::handle:vertical:pressed {
background: rgba(0, 0, 0, 0.5);
}
QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical {
height: 0px;
}
QScrollBar::add-page:vertical,
QScrollBar::sub-page:vertical {
background: transparent;
}
/* === QScrollBar Horizontal === */
QScrollBar:horizontal {
background: transparent;
height: 12px;
margin: 0;
}
QScrollBar::handle:horizontal {
background: rgba(0, 0, 0, 0.25);
min-width: 30px;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:horizontal:hover {
background: rgba(0, 0, 0, 0.4);
}
QScrollBar::handle:horizontal:pressed {
background: rgba(0, 0, 0, 0.5);
}
QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal {
width: 0px;
}
QScrollBar::add-page:horizontal,
QScrollBar::sub-page:horizontal {
background: transparent;
}
/* === QToolTip === */
QToolTip {
background-color: #ffffff;
color: #1f1f1f;
border: 1px solid #d1d1d1;
border-radius: 4px;
padding: 6px 8px;
font-size: 13px;
}
/* === Corner Widget (zwischen Scrollbars) === */
QTableView QTableCornerButton::section {
background-color: #fafafa;
border: none;
border-right: 1px solid #e1e1e1;
border-bottom: 1px solid #e1e1e1;
}
/* QLineEdit */
QLineEdit {
background-color: #ffffff;
color: #1f1f1f;
border: 1px solid #d1d1d1;
border-radius: 4px;
padding: 6px 8px;
selection-background-color: #0078d4;
selection-color: #ffffff;
}
QLineEdit:hover {
border: 1px solid #a0a0a0;
}
/*
QLineEdit:focus {
border: 1px solid #0078d4;
border-bottom: 2px solid #0078d4;
}
*/
QLineEdit:disabled {
background-color: #fafafa;
color: #a0a0a0;
border: 1px solid #e1e1e1;
}

View File

@@ -2,68 +2,65 @@
<Bike name='franken-wheeler'>
<Device Type="Console">
<Value ID='Cons_Rev_Hw' Label='Hardware Version' />
<Value ID='Cons_Rev_Sw' Label='Software Version' />
<Value ID='Cons_Sn_Product_Hi' Label='Product Number' IsWord='1'/>
<Value ID='Cons_Sn_Oem_Hi' Label='OEM Number' IsWord='1' />
<Value ID='Cons_Assist_Initlevel' Label='Assistance Init Level' Min='0' Max='4'/>
<Value ID='Cons_Assist_Level_1' Label='Assistance Level 1' Factor='1.5625' UnitLabel='%' Min='0' Max='400' />
<Value ID='Cons_Assist_Level_2' Label='Assistance Level 2' Factor='1.5625' UnitLabel='%' Min='0' Max='400' />
<Value ID='Cons_Assist_Level_3' Label='Assistance Level 3' Factor='1.5625' UnitLabel='%' Min='0' Max='400' />
<Value ID='Cons_Assist_Level_4' Label='Assistance Level 4' Factor='1.5625' UnitLabel='%' Min='0' Max='400' />
<Value ID='Cons_Assist_Maxspeed_Flag' Label='Max Limit Enabled' ValueType='bool' />
<Value ID='Cons_Assist_Maxspeed_Hi' Label='Max Speed Limit' UnitLabel='km/h' Factor='0.1' Min='0' Max='70'/>
<Value ID='Cons_Assist_Minspeed_Flag' Label='Min Limit Enabled' ValueType='bool' />
<Value ID='Cons_Assist_Minspeed' Label='Min Speed Limit' UnitLabel='km/h' Min='0' Max='70' />
<Value ID='Cons_Throttle_Maxspeed_Flag' Label='Throttle Limit Enabled' ValueType='bool'/>
<Value ID='Cons_Throttle_Maxspeed_Hi' Label='Throttle Speed Limit' UnitLabel='km/h' Factor='0.1' Min='0' Max='70' />
<Value ID='Cons_Geometry_Circ_Hi' Label='Wheel Circumference' IsWord='1' UnitLabel='mm' Min='0' Max='2300' Factor='1.5625' />
<Value ID='Cons_Assist_Mountain_Cap' Label='Mountain Cap' UnitLabel='%' Factor='1.5625' />
<Device Type='Motor'>
<Value ID='Motor_Rev_Hw' Label='Hardware Version' ValueType='Plain' ReadOnly='true' />
<Value ID='Motor_Rev_Sw' Label='Software Version' ValueType='Plain' ReadOnly='true' />
<Value ID='Motor_Sn_Item_Hi' Label='Motor Part Number' ValueType='Plain' IsWord='true' ReadOnly='true' />
<Value ID='Motor_Sn_Item_Hi' Label='Motor Serial Number' ValueType='Plain' IsWord='true' ReadOnly='true' />
<Value ID='Motor_Status_Temperature' Label='Motor Temperature' ValueType='Float' UnitLabel='°C' />
<Value ID='Motor_Assist_Maxspeed' Label='Motor max. Speed' UnitLabel='km/h' Factor='0.1' Min='0' Max='70' ValueType='Float' />
<Value ID='Motor_Geometry_Circ_Hi' Label='Wheel Circumference' IsWord='true' UnitLabel='mm' Min='0' Max='2300' ValueType='Number' Factor='1.5625' />
</Device>
<Device Type="Motor">
<Value ID='Motor_Rev_Hw' Label='Hardware Version' />
<Value ID='Motor_Rev_Sw' Label='Software Version' />
<Value ID='Motor_Sn_Item_Hi' Label='Motor Part Number' IsWord='1'/>
<Value ID='Motor_Sn_Item_Hi' Label='Motor Serial Number' IsWord='1' />
<Value ID='Motor_Status_Temperature' Label='Motor Temperature' UnitLabel='°C' />
<Value ID='Motor_Assist_Maxspeed' Label='Motor max. Speed' Reader='Kmh' />
<Value ID='Motor_Geometry_Circ_Hi' Label='Wheel Circumference' IsWord='1' UnitLabel='mm' Min='0' Max='2300' Factor='1.5625' />
<Device Type='Battery'>
<Value ID='Reg_Battery_Rev_Hw' Label='Hardware Version' ValueType='Plain' ReadOnly='true' />
<Value ID='Reg_Battery_Rev_Sw' Label='Software Version' ValueType='Plain' ReadOnly='true' />
</Device>
<Device Type="Battery">
<Value ID='Battery_Rev_Hw' Label='Hardware Version' />
<Value ID='Battery_Rev_Sw' Label='Software Version' />
<Device Type='Console'>
<Value ID='Cons_Rev_Hw' Label='Hardware Version' ValueType='Plain' ReadOnly='true' />
<Value ID='Cons_Rev_Sw' Label='Software Version' ValueType='Plain' ReadOnly='true' />
<Value ID='Cons_Sn_Product_Hi' Label='Product Number' IsWord='true' ValueType='Plain' ReadOnly='true'/>
<Value ID='Cons_Sn_Oem_Hi' Label='OEM Number' IsWord='true' ValueType='Plain' ReadOnly='true' />
<!--<Value ID='Cons_Assist_Initlevel' Label='Assistance Init Level' Min='0' Max='4' ValueType='Number'/>-->
<Value ID='Cons_Assist_Initlevel' Label='Assistance Init Level' Min='0' Max='100' UnitLabel='%' ValueType='Float'/>
<Value ID='Cons_Assist_Level_1' Label='Assistance Level 1' Factor='1.5625' UnitLabel='%' Min='0' Max='400' ValueType='Float'/>
<Value ID='Cons_Assist_Level_2' Label='Assistance Level 2' Factor='1.5625' UnitLabel='%' Min='0' Max='400' ValueType='Float'/>
<Value ID='Cons_Assist_Level_3' Label='Assistance Level 3' Factor='1.5625' UnitLabel='%' Min='0' Max='400' ValueType='Float'/>
<Value ID='Cons_Assist_Level_4' Label='Assistance Level 4' Factor='1.5625' UnitLabel='%' Min='0' Max='400' ValueType='Float'/>
<Value ID='Cons_Assist_Maxspeed_Flag' Label='Max Limit Enabled' ValueType='Bool' />
<Value ID='Cons_Assist_Maxspeed_Hi' Label='Max Speed Limit' UnitLabel='km/h' Factor='0.1' Min='0' Max='70' ValueType='Float'/>
<Value ID='Cons_Assist_Minspeed_Flag' Label='Min Limit Enabled' ValueType='Bool' />
<Value ID='Cons_Assist_Minspeed' Label='Min Speed Limit' UnitLabel='km/h' Min='0' Max='70' ValueType='Float'/>
<Value ID='Cons_Throttle_Maxspeed_Flag' Label='Throttle Limit Enabled' ValueType='Bool'/>
<Value ID='Cons_Throttle_Maxspeed_Hi' Label='Throttle Speed Limit' UnitLabel='km/h' Factor='0.1' Min='0' Max='70' ValueType='Float'/>
<Value ID='Cons_Geometry_Circ_Hi' Label='Wheel Circumference' IsWord='true' UnitLabel='mm' Min='0' Max='2300' Factor='1.5625' ValueType='Number'/>
<Value ID='Cons_Assist_Mountain_Cap' Label='Mountain Cap' UnitLabel='%' Factor='1.5625' ValueType='Float'/>
</Device>
</Bike>
<!--
printf( " odo .....................: Percent0.2f Km" _NL _NL,
((getValue(CONSOLE, CONSOLE_STATS_BCValueTypeWord_1) << 24) +
((getValue(CONSOLE, CONSOLE_STATS_BCValueTypeWord_1) << 24) +
(getValue(CONSOLE, CONSOLE_STATS_BCValueTypeWord_2) << 16) +
(getValue(CONSOLE, CONSOLE_STATS_BCValueTypeWord_3) << 8) +
(getValue(CONSOLE, CONSOLE_STATS_BCValueTypeWord_4))) / (double)10
);
}
<Value ID='Cons_Stat_Dist_Hi' Label='' Reader='mm' Factor='0.1' />
<Value ID='Cons_Stat_Dist_Lo' Label='' Reader='mm'/>
<Value ID='Cons_Stat_Dist_Lo' Label='' Reader='mm'/>
<Value ID='Cons_Stat_Avgspeed_Hi' Label='' Reader='mm' Factor='0.1' />
<Value ID='Cons_Stat_Avgspeed_Lo' Label='' Reader='mm'/>
@@ -87,7 +84,7 @@
<Value ID='Cons_Sn_Day' Label='' />
<Value ID='Cons_Sn_Pn_Hi' Label='' />
<Value ID='Cons_Sn_Pn_Lo' Label='' />
<Value ID='Cons_Sn_Pn_Lo' Label='' />
<Value ID='Cons_Sn_Item_Hi' Label='' />
<Value ID='Cons_Sn_Item_Lo' Label='' />
@@ -133,7 +130,7 @@
<Value ID='Cons_Preference_Codesrw_Lolo' Label='' />
<Value ID='Cons_Preference_Throttle_Mode' Label='' />
<Value ID='Cons_Throttle_Boost_Triggerlevel' Label='' Min='1.5' Max='50.0' Factor='1.5625' />
<Value ID='Cons_Preference_Flip_Side' Label='' />
<Value ID='Cons_Config_Testmode' Label='' />
@@ -141,25 +138,25 @@
<Value ID='Cons_Config_Last_Mode' Label='' />
<Value ID='Cons_Assist_Speedgain' Label='' Factor='0.1' />
<Value ID='Cons_Config_Last_Mode_On' Label='' />
<Value ID='Cons_Config_Last_Mode_Off' Label='' />
<Value ID='Cons_Config_Last_Mode_Off' Label='' />
<Value ID='Cons_Status_Slave' Label='' />
<Value ID='Cons_Throttle_Raw_Hi' Label='' />
<Value ID='Cons_Throttle_Raw_Lo' Label='' />
<Value ID='Cons_Throttle_Raw_Lo' Label='' />
<Value ID='Cons_Throttle_Position' Label='' Factor='1.5625'/>
<Value ID='Cons_Assist_Level_Rekuperation_3' Label='' Factor='1.5625'/>
<Value ID='Cons_Assist_Level_Rekuperation_4' Label='' Factor='1.5625'/>
<Value ID='Cons_Assist_Level_Rekuperation_4' Label='' Factor='1.5625'/>
<Value ID='Cons_Config_Service_Timestamp_Hi' Label='' />
<Value ID='Cons_Config_Service_Zimestamp_Lo' Label='' />
<Value ID='Cons_Config_Service_Distance_Hi' Label='' />
<Value ID='Cons_Config_Service_Distance_Lo' Label='' />
<Value ID='Cons_Assist_Level_Rekuperation_1' Label='' Factor='1.5625'/>
<Value ID='Cons_Assist_Level_Rekuperation_2' Label='' Factor='1.5625'/>
<Value ID='Cons_Assist_Level_Rekuperation_2' Label='' Factor='1.5625'/>
-->

View File

@@ -1,167 +0,0 @@
/* appqss */
/* Alle QWidgets bekommen diesen Font */
QWidget
{
font-family: "Calibri", "Carlito", "Open Sans", sans-serif;
font-size: 10pt;
margin: 0px;
padding: 0px;
}
QLabel#_headerLabel
{
font-size: 14pt;
font-weight: bold;
}
/*
QToolButton
{
background-color: white;
color: #201F1E;
border: 1px solid #8A8886;
border-radius: 4px;
min-width: 64px;
max-width: 64px;
min-height: 64px;
max-height: 64px;
}
QToolButton:hover
{
background-color: #F3F2F1;
border: 1px solid #323130;
}
QToolButton:pressed
{
background-color: #EDEBE9;
border: 1px solid #201F1E;
}
QToolButton:disabled
{
background-color: #F3F2F1;
color: #A19F9D;
border: 1px solid #EDEBE9;
}
*/
/* === QToolButton === */
QToolButton
{
background-color: transparent;
color: #000000;
border: none;
border-radius: 4px;
padding: 6px;
min-width: 64px;
max-width: 64px;
min-height: 64px;
max-height: 64px;
}
QToolButton:hover
{
background-color: #F9F9F9;
border: 1px solid #DDDDDD;
}
QToolButton:pressed
{
background-color: #E0E0E0;
}
QToolButton:checked
{
background-color: green;/*#0078D4;*/
color: #FFFFFF;
}
QToolButton:disabled
{
color: #A19F9D;
}
/* === QTableView & QTableWidget === */
QTableView, QTableWidget
{
background-color: #FFFFFF;
color: #000000;
gridline-color: #E1DFDD;
border: 1px solid #E1DFDD;
border-radius: 4px;
selection-background-color: #0078D4;
}
QTableView::item:hover
{
background-color: #e8f4f8;
}
QScrollBar::handle:horizontal {
background-color: #C8C6C4;
min-width: 40px;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:horizontal:hover {
background-color: #A19F9D;
}
QScrollBar::handle:horizontal:pressed {
background-color: #8A8886;
}
QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal {
width: 0px;
}
QScrollBar::add-page:horizontal,
QScrollBar::sub-page:horizontal {
background: none;
}
QScrollBar:vertical {
background-color: transparent;
width: 12px;
margin: 0;
}
QScrollBar::handle:vertical {
background-color: #C8C6C4;
min-height: 40px;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:vertical:hover {
background-color: #A19F9D;
}
QScrollBar::handle:vertical:pressed {
background-color: #8A8886;
}
QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical {
height: 0px;
}
QScrollBar::add-page:vertical,
QScrollBar::sub-page:vertical {
background: none;
}

View File

@@ -1,871 +0,0 @@
/* ===================================================================
Fluent Design Dark Mode Stylesheet for Qt6
Windows 11 inspired dark theme
=================================================================== */
/* === Color Palette ===
Background: #202020
Surface: #2B2B2B
Surface Hover: #3A3A3A
Border: #3F3F3F
Text: #FFFFFF
Text Secondary: #B0B0B0
Accent: #0078D4
Accent Hover: #106EBE
=== */
/* === Global Styles === */
* {
font-family: "Segoe UI", "Noto Sans", Roboto, sans-serif;
font-size: 9pt;
}
QWidget {
background-color: #202020;
color: #FFFFFF;
selection-background-color: #0078D4;
selection-color: #FFFFFF;
}
/* === QMainWindow === */
QMainWindow {
background-color: #202020;
}
QMainWindow::separator {
background-color: #3F3F3F;
width: 1px;
height: 1px;
}
QMainWindow::separator:hover {
background-color: #0078D4;
}
/* === QPushButton === */
QPushButton {
background-color: #2B2B2B;
color: #FFFFFF;
border: 1px solid #3F3F3F;
border-radius: 4px;
padding: 6px 16px;
min-height: 20px;
font-weight: 500;
}
QPushButton:hover {
background-color: #3A3A3A;
border-color: #5A5A5A;
}
QPushButton:pressed {
background-color: #1F1F1F;
border-color: #3F3F3F;
}
QPushButton:disabled {
background-color: #2B2B2B;
color: #5A5A5A;
border-color: #3F3F3F;
}
QPushButton:default {
background-color: #0078D4;
border-color: #0078D4;
color: #FFFFFF;
}
QPushButton:default:hover {
background-color: #106EBE;
border-color: #106EBE;
}
QPushButton:default:pressed {
background-color: #005A9E;
border-color: #005A9E;
}
/* === QLineEdit === */
QLineEdit {
background-color: #2B2B2B;
color: #FFFFFF;
border: 1px solid #3F3F3F;
border-radius: 4px;
padding: 6px 8px;
selection-background-color: #0078D4;
}
QLineEdit:hover {
border-color: #5A5A5A;
}
QLineEdit:focus {
background-color: #1F1F1F;
border: 2px solid #0078D4;
padding: 5px 7px;
}
QLineEdit:disabled {
background-color: #2B2B2B;
color: #5A5A5A;
border-color: #3F3F3F;
}
QLineEdit[readOnly="true"] {
background-color: #2B2B2B;
color: #B0B0B0;
}
/* === QTextEdit & QPlainTextEdit === */
QTextEdit, QPlainTextEdit {
background-color: #2B2B2B;
color: #FFFFFF;
border: 1px solid #3F3F3F;
border-radius: 4px;
padding: 8px;
selection-background-color: #0078D4;
}
QTextEdit:hover, QPlainTextEdit:hover {
border-color: #5A5A5A;
}
QTextEdit:focus, QPlainTextEdit:focus {
border: 2px solid #0078D4;
padding: 7px;
}
QTextEdit:disabled, QPlainTextEdit:disabled {
background-color: #2B2B2B;
color: #5A5A5A;
}
/* === QComboBox === */
QComboBox {
background-color: #2B2B2B;
color: #FFFFFF;
border: 1px solid #3F3F3F;
border-radius: 4px;
padding: 6px 8px;
min-height: 20px;
}
QComboBox:hover {
background-color: #3A3A3A;
border-color: #5A5A5A;
}
QComboBox:focus {
border: 2px solid #0078D4;
}
QComboBox:disabled {
background-color: #2B2B2B;
color: #5A5A5A;
}
QComboBox::drop-down {
border: none;
width: 20px;
}
QComboBox::down-arrow {
image: url(:/icons/down-arrow.png);
width: 12px;
height: 12px;
}
QComboBox QAbstractItemView {
background-color: #2B2B2B;
color: #FFFFFF;
border: 1px solid #3F3F3F;
selection-background-color: #0078D4;
selection-color: #FFFFFF;
outline: none;
}
QComboBox QAbstractItemView::item {
min-height: 28px;
padding-left: 8px;
}
QComboBox QAbstractItemView::item:hover {
background-color: #3A3A3A;
}
/* === QSpinBox & QDoubleSpinBox === */
QSpinBox, QDoubleSpinBox {
background-color: #2B2B2B;
color: #FFFFFF;
border: 1px solid #3F3F3F;
border-radius: 4px;
padding: 6px 8px;
min-height: 20px;
}
QSpinBox:hover, QDoubleSpinBox:hover {
border-color: #5A5A5A;
}
QSpinBox:focus, QDoubleSpinBox:focus {
border: 2px solid #0078D4;
}
QSpinBox:disabled, QDoubleSpinBox:disabled {
background-color: #2B2B2B;
color: #5A5A5A;
}
QSpinBox::up-button, QDoubleSpinBox::up-button {
background-color: transparent;
border: none;
width: 16px;
}
QSpinBox::up-button:hover, QDoubleSpinBox::up-button:hover {
background-color: #3A3A3A;
}
QSpinBox::down-button, QDoubleSpinBox::down-button {
background-color: transparent;
border: none;
width: 16px;
}
QSpinBox::down-button:hover, QDoubleSpinBox::down-button:hover {
background-color: #3A3A3A;
}
/* === QCheckBox === */
QCheckBox {
spacing: 8px;
color: #FFFFFF;
}
QCheckBox::indicator {
width: 18px;
height: 18px;
border-radius: 4px;
border: 1px solid #5A5A5A;
background-color: #2B2B2B;
}
QCheckBox::indicator:hover {
background-color: #3A3A3A;
border-color: #6A6A6A;
}
QCheckBox::indicator:checked {
background-color: #0078D4;
border-color: #0078D4;
image: url(:/icons/checkmark.png);
}
QCheckBox::indicator:checked:hover {
background-color: #106EBE;
border-color: #106EBE;
}
QCheckBox::indicator:disabled {
background-color: #2B2B2B;
border-color: #3F3F3F;
}
QCheckBox:disabled {
color: #5A5A5A;
}
/* === QRadioButton === */
QRadioButton {
spacing: 8px;
color: #FFFFFF;
}
QRadioButton::indicator {
width: 18px;
height: 18px;
border-radius: 9px;
border: 1px solid #5A5A5A;
background-color: #2B2B2B;
}
QRadioButton::indicator:hover {
background-color: #3A3A3A;
border-color: #6A6A6A;
}
QRadioButton::indicator:checked {
background-color: #0078D4;
border-color: #0078D4;
}
QRadioButton::indicator:checked:hover {
background-color: #106EBE;
border-color: #106EBE;
}
QRadioButton::indicator:checked::after {
width: 8px;
height: 8px;
border-radius: 4px;
background-color: #FFFFFF;
}
QRadioButton::indicator:disabled {
background-color: #2B2B2B;
border-color: #3F3F3F;
}
QRadioButton:disabled {
color: #5A5A5A;
}
/* === QSlider === */
QSlider::groove:horizontal {
height: 4px;
background-color: #3F3F3F;
border-radius: 2px;
}
QSlider::handle:horizontal {
background-color: #FFFFFF;
border: 2px solid #0078D4;
width: 16px;
height: 16px;
margin: -7px 0;
border-radius: 8px;
}
QSlider::handle:horizontal:hover {
background-color: #FFFFFF;
border: 2px solid #106EBE;
}
QSlider::handle:horizontal:pressed {
background-color: #E0E0E0;
}
QSlider::sub-page:horizontal {
background-color: #0078D4;
border-radius: 2px;
}
QSlider::groove:vertical {
width: 4px;
background-color: #3F3F3F;
border-radius: 2px;
}
QSlider::handle:vertical {
background-color: #FFFFFF;
border: 2px solid #0078D4;
width: 16px;
height: 16px;
margin: 0 -7px;
border-radius: 8px;
}
QSlider::sub-page:vertical {
background-color: #0078D4;
border-radius: 2px;
}
/* === QProgressBar === */
QProgressBar {
background-color: #3F3F3F;
border: none;
border-radius: 2px;
height: 4px;
text-align: center;
color: transparent;
}
QProgressBar::chunk {
background-color: #0078D4;
border-radius: 2px;
}
QProgressBar:disabled {
background-color: #3F3F3F;
}
/* === QScrollBar === */
QScrollBar:horizontal {
background-color: transparent;
height: 12px;
margin: 0;
}
QScrollBar::handle:horizontal {
background-color: #5A5A5A;
min-width: 40px;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:horizontal:hover {
background-color: #6A6A6A;
}
QScrollBar::handle:horizontal:pressed {
background-color: #7A7A7A;
}
QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal {
width: 0px;
}
QScrollBar::add-page:horizontal,
QScrollBar::sub-page:horizontal {
background: none;
}
QScrollBar:vertical {
background-color: transparent;
width: 12px;
margin: 0;
}
QScrollBar::handle:vertical {
background-color: #5A5A5A;
min-height: 40px;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:vertical:hover {
background-color: #6A6A6A;
}
QScrollBar::handle:vertical:pressed {
background-color: #7A7A7A;
}
QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical {
height: 0px;
}
QScrollBar::add-page:vertical,
QScrollBar::sub-page:vertical {
background: none;
}
/* === QTabWidget === */
QTabWidget::pane {
background-color: #2B2B2B;
border: 1px solid #3F3F3F;
border-radius: 4px;
top: -1px;
}
QTabBar::tab {
background-color: transparent;
color: #B0B0B0;
border: none;
border-bottom: 2px solid transparent;
padding: 8px 16px;
margin-right: 4px;
}
QTabBar::tab:hover {
color: #FFFFFF;
background-color: #3A3A3A;
border-bottom: 2px solid #5A5A5A;
}
QTabBar::tab:selected {
color: #FFFFFF;
background-color: #2B2B2B;
border-bottom: 2px solid #0078D4;
}
QTabBar::tab:disabled {
color: #5A5A5A;
}
/* === QGroupBox === */
QGroupBox {
background-color: #2B2B2B;
border: 1px solid #3F3F3F;
border-radius: 6px;
margin-top: 12px;
padding-top: 12px;
font-weight: 600;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top left;
padding: 0 8px;
color: #FFFFFF;
background-color: #2B2B2B;
}
/* === QLabel === */
QLabel {
background-color: transparent;
color: #FFFFFF;
}
QLabel:disabled {
color: #5A5A5A;
}
/* === QToolButton === */
QToolButton {
background-color: transparent;
color: #FFFFFF;
border: none;
border-radius: 4px;
padding: 6px;
}
QToolButton:hover {
background-color: #3A3A3A;
}
QToolButton:pressed {
background-color: #1F1F1F;
}
QToolButton:checked {
background-color: #0078D4;
}
QToolButton:disabled {
color: #5A5A5A;
}
/* === QMenu === */
QMenu {
background-color: #2B2B2B;
color: #FFFFFF;
border: 1px solid #3F3F3F;
padding: 4px;
}
QMenu::item {
background-color: transparent;
padding: 6px 24px 6px 8px;
border-radius: 4px;
}
QMenu::item:selected {
background-color: #3A3A3A;
}
QMenu::item:disabled {
color: #5A5A5A;
}
QMenu::separator {
height: 1px;
background-color: #3F3F3F;
margin: 4px 0;
}
QMenu::icon {
padding-left: 8px;
}
/* === QMenuBar === */
QMenuBar {
background-color: #2B2B2B;
color: #FFFFFF;
border-bottom: 1px solid #3F3F3F;
}
QMenuBar::item {
background-color: transparent;
padding: 6px 12px;
border-radius: 4px;
}
QMenuBar::item:selected {
background-color: #3A3A3A;
}
QMenuBar::item:pressed {
background-color: #1F1F1F;
}
/* === QToolBar === */
QToolBar {
background-color: #2B2B2B;
border: none;
border-bottom: 1px solid #3F3F3F;
spacing: 4px;
padding: 4px;
}
QToolBar::handle {
background-color: #3F3F3F;
width: 1px;
height: 1px;
margin: 4px;
}
QToolBar::separator {
background-color: #3F3F3F;
width: 1px;
height: 1px;
margin: 4px;
}
/* === QStatusBar === */
QStatusBar {
background-color: #2B2B2B;
color: #B0B0B0;
border-top: 1px solid #3F3F3F;
}
QStatusBar::item {
border: none;
}
/* === QDockWidget === */
QDockWidget {
background-color: #2B2B2B;
color: #FFFFFF;
titlebar-close-icon: url(:/icons/close.png);
titlebar-normal-icon: url(:/icons/float.png);
}
QDockWidget::title {
background-color: #2B2B2B;
border: 1px solid #3F3F3F;
padding: 6px;
text-align: left;
}
QDockWidget::close-button,
QDockWidget::float-button {
background-color: transparent;
border: none;
padding: 4px;
}
QDockWidget::close-button:hover,
QDockWidget::float-button:hover {
background-color: #3A3A3A;
border-radius: 4px;
}
/* === QListView & QListWidget === */
QListView, QListWidget {
background-color: #2B2B2B;
color: #FFFFFF;
border: 1px solid #3F3F3F;
border-radius: 4px;
outline: none;
}
QListView::item, QListWidget::item {
padding: 6px;
border-radius: 4px;
}
QListView::item:hover, QListWidget::item:hover {
background-color: #3A3A3A;
}
QListView::item:selected, QListWidget::item:selected {
background-color: #0078D4;
color: #FFFFFF;
}
QListView::item:disabled, QListWidget::item:disabled {
color: #5A5A5A;
}
/* === QTreeView & QTreeWidget === */
QTreeView, QTreeWidget {
background-color: #2B2B2B;
color: #FFFFFF;
border: 1px solid #3F3F3F;
border-radius: 4px;
outline: none;
show-decoration-selected: 1;
}
QTreeView::item, QTreeWidget::item {
padding: 4px;
border-radius: 4px;
}
QTreeView::item:hover, QTreeWidget::item:hover {
background-color: #3A3A3A;
}
QTreeView::item:selected, QTreeWidget::item:selected {
background-color: #0078D4;
color: #FFFFFF;
}
QTreeView::branch, QTreeWidget::branch {
background-color: transparent;
}
QTreeView::branch:closed:has-children, QTreeWidget::branch:closed:has-children {
image: url(:/icons/branch-closed.png);
}
QTreeView::branch:open:has-children, QTreeWidget::branch:open:has-children {
image: url(:/icons/branch-open.png);
}
/* === QTableView & QTableWidget === */
QTableView, QTableWidget {
background-color: #2B2B2B;
color: #FFFFFF;
gridline-color: #3F3F3F;
border: 1px solid #3F3F3F;
border-radius: 4px;
selection-background-color: #0078D4;
}
QTableView::item, QTableWidget::item {
padding: 4px;
}
QTableView::item:hover, QTableWidget::item:hover {
background-color: #3A3A3A;
}
QTableView::item:selected, QTableWidget::item:selected {
background-color: #0078D4;
color: #FFFFFF;
}
QHeaderView::section {
background-color: #2B2B2B;
color: #FFFFFF;
padding: 6px;
border: none;
border-right: 1px solid #3F3F3F;
border-bottom: 1px solid #3F3F3F;
font-weight: 600;
}
QHeaderView::section:hover {
background-color: #3A3A3A;
}
/* === QDialog === */
QDialog {
background-color: #2B2B2B;
}
/* === QMessageBox === */
QMessageBox {
background-color: #2B2B2B;
}
QMessageBox QLabel {
color: #FFFFFF;
}
/* === QToolTip === */
QToolTip {
background-color: #3A3A3A;
color: #FFFFFF;
border: 1px solid #5A5A5A;
border-radius: 4px;
padding: 4px 8px;
}
/* === QCalendarWidget === */
QCalendarWidget {
background-color: #2B2B2B;
}
QCalendarWidget QToolButton {
color: #FFFFFF;
background-color: transparent;
border: none;
border-radius: 4px;
padding: 4px;
}
QCalendarWidget QToolButton:hover {
background-color: #3A3A3A;
}
QCalendarWidget QMenu {
background-color: #2B2B2B;
}
QCalendarWidget QSpinBox {
background-color: #2B2B2B;
color: #FFFFFF;
selection-background-color: #0078D4;
}
QCalendarWidget QAbstractItemView {
background-color: #2B2B2B;
color: #FFFFFF;
selection-background-color: #0078D4;
selection-color: #FFFFFF;
}
/* === QSplitter === */
QSplitter::handle {
background-color: #3F3F3F;
}
QSplitter::handle:hover {
background-color: #0078D4;
}
QSplitter::handle:horizontal {
width: 1px;
}
QSplitter::handle:vertical {
height: 1px;
}
/* === Custom Classes === */
.accent-button {
background-color: #0078D4;
color: #FFFFFF;
border: none;
}
.accent-button:hover {
background-color: #106EBE;
}
.accent-button:pressed {
background-color: #005A9E;
}
.danger-button {
background-color: #C42B1C;
color: #FFFFFF;
border: none;
}
.danger-button:hover {
background-color: #A52314;
}
.card {
background-color: #2B2B2B;
border: 1px solid #3F3F3F;
border-radius: 8px;
padding: 16px;
}
.surface {
background-color: #2B2B2B;
border: 1px solid #3F3F3F;
}
.divider {
background-color: #3F3F3F;
min-height: 1px;
max-height: 1px;
}

View File

@@ -1,890 +0,0 @@
/* ===================================================================
Fluent Design Light Mode Stylesheet for Qt6
Windows 11 inspired light theme
=================================================================== */
/* === Color Palette ===
Background: #F3F3F3
Surface: #FFFFFF
Surface Hover: #F9F9F9
Border: #E1DFDD
Text: #000000
Text Secondary: #605E5C
Accent: #0078D4
Accent Hover: #106EBE
=== */
/* === Global Styles === */
* {
font-family: "Segoe UI", "Noto Sans", Roboto, sans-serif;
font-size: 9pt;
}
QWidget {
background-color: #F3F3F3;
color: #000000;
selection-background-color: #0078D4;
selection-color: #FFFFFF;
}
/* === QMainWindow === */
QMainWindow {
background-color: #F3F3F3;
}
QMainWindow::separator {
background-color: #E1DFDD;
width: 1px;
height: 1px;
}
QMainWindow::separator:hover {
background-color: #0078D4;
}
/* === QPushButton === */
QPushButton {
background-color: #FFFFFF;
color: #000000;
border: 1px solid #E1DFDD;
border-radius: 4px;
padding: 6px 16px;
min-height: 20px;
font-weight: 500;
}
QPushButton:hover {
background-color: #F9F9F9;
border-color: #D1CFCD;
}
QPushButton:pressed {
background-color: #F0F0F0;
border-color: #E1DFDD;
}
QPushButton:disabled {
background-color: #F9F9F9;
color: #A19F9D;
border-color: #E1DFDD;
}
QPushButton:default {
background-color: #0078D4;
border-color: #0078D4;
color: #FFFFFF;
}
QPushButton:default:hover {
background-color: #106EBE;
border-color: #106EBE;
}
QPushButton:default:pressed {
background-color: #005A9E;
border-color: #005A9E;
}
/* === QLineEdit === */
QLineEdit {
background-color: #FFFFFF;
color: #000000;
border: 1px solid #E1DFDD;
border-radius: 4px;
padding: 6px 8px;
selection-background-color: #0078D4;
}
QLineEdit:hover {
border-color: #D1CFCD;
}
QLineEdit:focus {
background-color: #FFFFFF;
border: 2px solid #0078D4;
padding: 5px 7px;
}
QLineEdit:disabled {
background-color: #F9F9F9;
color: #A19F9D;
border-color: #E1DFDD;
}
QLineEdit[readOnly="true"] {
background-color: #F9F9F9;
color: #605E5C;
}
/* === QTextEdit & QPlainTextEdit === */
QTextEdit, QPlainTextEdit {
background-color: #FFFFFF;
color: #000000;
border: 1px solid #E1DFDD;
border-radius: 4px;
padding: 8px;
selection-background-color: #0078D4;
}
QTextEdit:hover, QPlainTextEdit:hover {
border-color: #D1CFCD;
}
QTextEdit:focus, QPlainTextEdit:focus {
border: 2px solid #0078D4;
padding: 7px;
}
QTextEdit:disabled, QPlainTextEdit:disabled {
background-color: #F9F9F9;
color: #A19F9D;
}
/* === QComboBox === */
QComboBox {
background-color: #FFFFFF;
color: #000000;
border: 1px solid #E1DFDD;
border-radius: 4px;
padding: 6px 8px;
min-height: 20px;
}
QComboBox:hover {
background-color: #F9F9F9;
border-color: #D1CFCD;
}
QComboBox:focus {
border: 2px solid #0078D4;
}
QComboBox:disabled {
background-color: #F9F9F9;
color: #A19F9D;
}
QComboBox::drop-down {
border: none;
width: 20px;
}
QComboBox::down-arrow {
image: url(:/icons/down-arrow-light.png);
width: 12px;
height: 12px;
}
QComboBox QAbstractItemView {
background-color: #FFFFFF;
color: #000000;
border: 1px solid #E1DFDD;
selection-background-color: #0078D4;
selection-color: #FFFFFF;
outline: none;
}
QComboBox QAbstractItemView::item {
min-height: 28px;
padding-left: 8px;
}
QComboBox QAbstractItemView::item:hover {
background-color: #F9F9F9;
}
/* === QSpinBox & QDoubleSpinBox === */
QSpinBox, QDoubleSpinBox {
background-color: #FFFFFF;
color: #000000;
border: 1px solid #E1DFDD;
border-radius: 4px;
padding: 6px 8px;
min-height: 20px;
}
QSpinBox:hover, QDoubleSpinBox:hover {
border-color: #D1CFCD;
}
QSpinBox:focus, QDoubleSpinBox:focus {
border: 2px solid #0078D4;
}
QSpinBox:disabled, QDoubleSpinBox:disabled {
background-color: #F9F9F9;
color: #A19F9D;
}
QSpinBox::up-button, QDoubleSpinBox::up-button {
background-color: transparent;
border: none;
width: 16px;
}
QSpinBox::up-button:hover, QDoubleSpinBox::up-button:hover {
background-color: #F0F0F0;
}
QSpinBox::down-button, QDoubleSpinBox::down-button {
background-color: transparent;
border: none;
width: 16px;
}
QSpinBox::down-button:hover, QDoubleSpinBox::down-button:hover {
background-color: #F0F0F0;
}
/* === QCheckBox === */
QCheckBox {
spacing: 8px;
color: #000000;
}
QCheckBox::indicator {
width: 18px;
height: 18px;
border-radius: 4px;
border: 1px solid #8A8886;
background-color: #FFFFFF;
}
QCheckBox::indicator:hover {
background-color: #F9F9F9;
border-color: #605E5C;
}
QCheckBox::indicator:checked {
background-color: #0078D4;
border-color: #0078D4;
image: url(:/icons/checkmark-white.png);
}
QCheckBox::indicator:checked:hover {
background-color: #106EBE;
border-color: #106EBE;
}
QCheckBox::indicator:disabled {
background-color: #F9F9F9;
border-color: #E1DFDD;
}
QCheckBox:disabled {
color: #A19F9D;
}
/* === QRadioButton === */
QRadioButton {
spacing: 8px;
color: #000000;
}
QRadioButton::indicator {
width: 18px;
height: 18px;
border-radius: 9px;
border: 1px solid #8A8886;
background-color: #FFFFFF;
}
QRadioButton::indicator:hover {
background-color: #F9F9F9;
border-color: #605E5C;
}
QRadioButton::indicator:checked {
background-color: #0078D4;
border-color: #0078D4;
}
QRadioButton::indicator:checked:hover {
background-color: #106EBE;
border-color: #106EBE;
}
QRadioButton::indicator:checked::after {
width: 8px;
height: 8px;
border-radius: 4px;
background-color: #FFFFFF;
}
QRadioButton::indicator:disabled {
background-color: #F9F9F9;
border-color: #E1DFDD;
}
QRadioButton:disabled {
color: #A19F9D;
}
/* === QSlider === */
QSlider::groove:horizontal {
height: 4px;
background-color: #E1DFDD;
border-radius: 2px;
}
QSlider::handle:horizontal {
background-color: #FFFFFF;
border: 2px solid #0078D4;
width: 16px;
height: 16px;
margin: -7px 0;
border-radius: 8px;
}
QSlider::handle:horizontal:hover {
background-color: #FFFFFF;
border: 2px solid #106EBE;
}
QSlider::handle:horizontal:pressed {
background-color: #F0F0F0;
}
QSlider::sub-page:horizontal {
background-color: #0078D4;
border-radius: 2px;
}
QSlider::groove:vertical {
width: 4px;
background-color: #E1DFDD;
border-radius: 2px;
}
QSlider::handle:vertical {
background-color: #FFFFFF;
border: 2px solid #0078D4;
width: 16px;
height: 16px;
margin: 0 -7px;
border-radius: 8px;
}
QSlider::sub-page:vertical {
background-color: #0078D4;
border-radius: 2px;
}
/* === QProgressBar === */
QProgressBar {
background-color: #E1DFDD;
border: none;
border-radius: 2px;
height: 4px;
text-align: center;
color: transparent;
}
QProgressBar::chunk {
background-color: #0078D4;
border-radius: 2px;
}
QProgressBar:disabled {
background-color: #E1DFDD;
}
/* === QScrollBar === */
QScrollBar:horizontal {
background-color: transparent;
height: 12px;
margin: 0;
}
QScrollBar::handle:horizontal {
background-color: #C8C6C4;
min-width: 40px;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:horizontal:hover {
background-color: #A19F9D;
}
QScrollBar::handle:horizontal:pressed {
background-color: #8A8886;
}
QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal {
width: 0px;
}
QScrollBar::add-page:horizontal,
QScrollBar::sub-page:horizontal {
background: none;
}
QScrollBar:vertical {
background-color: transparent;
width: 12px;
margin: 0;
}
QScrollBar::handle:vertical {
background-color: #C8C6C4;
min-height: 40px;
border-radius: 6px;
margin: 2px;
}
QScrollBar::handle:vertical:hover {
background-color: #A19F9D;
}
QScrollBar::handle:vertical:pressed {
background-color: #8A8886;
}
QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical {
height: 0px;
}
QScrollBar::add-page:vertical,
QScrollBar::sub-page:vertical {
background: none;
}
/* === QTabWidget === */
QTabWidget::pane {
background-color: #FFFFFF;
border: 1px solid #E1DFDD;
border-radius: 4px;
top: -1px;
}
QTabBar::tab {
background-color: transparent;
color: #605E5C;
border: none;
border-bottom: 2px solid transparent;
padding: 8px 16px;
margin-right: 4px;
}
QTabBar::tab:hover {
color: #000000;
background-color: #F9F9F9;
border-bottom: 2px solid #C8C6C4;
}
QTabBar::tab:selected {
color: #000000;
background-color: #FFFFFF;
border-bottom: 2px solid #0078D4;
}
QTabBar::tab:disabled {
color: #A19F9D;
}
/* === QGroupBox === */
QGroupBox {
background-color: #FFFFFF;
border: 1px solid #E1DFDD;
border-radius: 6px;
margin-top: 12px;
padding-top: 12px;
font-weight: 600;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top left;
padding: 0 8px;
color: #000000;
background-color: #FFFFFF;
}
/* === QLabel === */
QLabel {
background-color: transparent;
color: #000000;
}
QLabel:disabled {
color: #A19F9D;
}
/* === QToolButton === */
QToolButton {
background-color: transparent;
color: #000000;
border: none;
border-radius: 4px;
padding: 6px;
}
QToolButton:hover {
background-color: #F9F9F9;
}
QToolButton:pressed {
background-color: #F0F0F0;
}
QToolButton:checked {
background-color: #0078D4;
color: #FFFFFF;
}
QToolButton:disabled {
color: #A19F9D;
}
/* === QMenu === */
QMenu {
background-color: #FFFFFF;
color: #000000;
border: 1px solid #E1DFDD;
padding: 4px;
}
QMenu::item {
background-color: transparent;
padding: 6px 24px 6px 8px;
border-radius: 4px;
}
QMenu::item:selected {
background-color: #F9F9F9;
}
QMenu::item:disabled {
color: #A19F9D;
}
QMenu::separator {
height: 1px;
background-color: #E1DFDD;
margin: 4px 0;
}
QMenu::icon {
padding-left: 8px;
}
/* === QMenuBar === */
QMenuBar {
background-color: #FFFFFF;
color: #000000;
border-bottom: 1px solid #E1DFDD;
}
QMenuBar::item {
background-color: transparent;
padding: 6px 12px;
border-radius: 4px;
}
QMenuBar::item:selected {
background-color: #F9F9F9;
}
QMenuBar::item:pressed {
background-color: #F0F0F0;
}
/* === QToolBar === */
QToolBar {
background-color: #FFFFFF;
border: none;
border-bottom: 1px solid #E1DFDD;
spacing: 4px;
padding: 4px;
}
QToolBar::handle {
background-color: #E1DFDD;
width: 1px;
height: 1px;
margin: 4px;
}
QToolBar::separator {
background-color: #E1DFDD;
width: 1px;
height: 1px;
margin: 4px;
}
/* === QStatusBar === */
QStatusBar {
background-color: #FFFFFF;
color: #605E5C;
border-top: 1px solid #E1DFDD;
}
QStatusBar::item {
border: none;
}
/* === QDockWidget === */
QDockWidget {
background-color: #FFFFFF;
color: #000000;
titlebar-close-icon: url(:/icons/close-light.png);
titlebar-normal-icon: url(:/icons/float-light.png);
}
QDockWidget::title {
background-color: #FFFFFF;
border: 1px solid #E1DFDD;
padding: 6px;
text-align: left;
}
QDockWidget::close-button,
QDockWidget::float-button {
background-color: transparent;
border: none;
padding: 4px;
}
QDockWidget::close-button:hover,
QDockWidget::float-button:hover {
background-color: #F9F9F9;
border-radius: 4px;
}
/* === QListView & QListWidget === */
QListView, QListWidget {
background-color: #FFFFFF;
color: #000000;
border: 1px solid #E1DFDD;
border-radius: 4px;
outline: none;
}
QListView::item, QListWidget::item {
padding: 6px;
border-radius: 4px;
}
QListView::item:hover, QListWidget::item:hover {
background-color: #F9F9F9;
}
QListView::item:selected, QListWidget::item:selected {
background-color: #0078D4;
color: #FFFFFF;
}
QListView::item:disabled, QListWidget::item:disabled {
color: #A19F9D;
}
/* === QTreeView & QTreeWidget === */
QTreeView, QTreeWidget {
background-color: #FFFFFF;
color: #000000;
border: 1px solid #E1DFDD;
border-radius: 4px;
outline: none;
show-decoration-selected: 1;
}
QTreeView::item, QTreeWidget::item {
padding: 4px;
border-radius: 4px;
}
QTreeView::item:hover, QTreeWidget::item:hover {
background-color: #F9F9F9;
}
QTreeView::item:selected, QTreeWidget::item:selected {
background-color: #0078D4;
color: #FFFFFF;
}
QTreeView::branch, QTreeWidget::branch {
background-color: transparent;
}
QTreeView::branch:closed:has-children, QTreeWidget::branch:closed:has-children {
image: url(:/icons/branch-closed-light.png);
}
QTreeView::branch:open:has-children, QTreeWidget::branch:open:has-children {
image: url(:/icons/branch-open-light.png);
}
/* === QTableView & QTableWidget === */
QTableView, QTableWidget {
background-color: #FFFFFF;
color: #000000;
gridline-color: #E1DFDD;
border: 1px solid #E1DFDD;
border-radius: 4px;
selection-background-color: #0078D4;
}
QTableView::item, QTableWidget::item {
padding: 4px;
}
QTableView::item:hover, QTableWidget::item:hover {
background-color: #F9F9F9;
}
QTableView::item:selected, QTableWidget::item:selected {
background-color: #0078D4;
color: #FFFFFF;
}
QHeaderView::section {
background-color: #FFFFFF;
color: #000000;
padding: 6px;
border: none;
border-right: 1px solid #E1DFDD;
border-bottom: 1px solid #E1DFDD;
font-weight: 600;
}
QHeaderView::section:hover {
background-color: #F9F9F9;
}
/* === QDialog === */
QDialog {
background-color: #FFFFFF;
}
/* === QMessageBox === */
QMessageBox {
background-color: #FFFFFF;
}
QMessageBox QLabel {
color: #000000;
}
/* === QToolTip === */
QToolTip {
background-color: #F9F9F9;
color: #000000;
border: 1px solid #E1DFDD;
border-radius: 4px;
padding: 4px 8px;
}
/* === QCalendarWidget === */
QCalendarWidget {
background-color: #FFFFFF;
}
QCalendarWidget QToolButton {
color: #000000;
background-color: transparent;
border: none;
border-radius: 4px;
padding: 4px;
}
QCalendarWidget QToolButton:hover {
background-color: #F9F9F9;
}
QCalendarWidget QMenu {
background-color: #FFFFFF;
}
QCalendarWidget QSpinBox {
background-color: #FFFFFF;
color: #000000;
selection-background-color: #0078D4;
}
QCalendarWidget QAbstractItemView {
background-color: #FFFFFF;
color: #000000;
selection-background-color: #0078D4;
selection-color: #FFFFFF;
}
/* === QSplitter === */
QSplitter::handle {
background-color: #E1DFDD;
}
QSplitter::handle:hover {
background-color: #0078D4;
}
QSplitter::handle:horizontal {
width: 1px;
}
QSplitter::handle:vertical {
height: 1px;
}
/* === Custom Classes === */
.accent-button {
background-color: #0078D4;
color: #FFFFFF;
border: none;
}
.accent-button:hover {
background-color: #106EBE;
}
.accent-button:pressed {
background-color: #005A9E;
}
.danger-button {
background-color: #C42B1C;
color: #FFFFFF;
border: none;
}
.danger-button:hover {
background-color: #A52314;
}
.success-button {
background-color: #107C10;
color: #FFFFFF;
border: none;
}
.success-button:hover {
background-color: #0E6B0E;
}
.card {
background-color: #FFFFFF;
border: 1px solid #E1DFDD;
border-radius: 8px;
padding: 16px;
}
.surface {
background-color: #FFFFFF;
border: 1px solid #E1DFDD;
}
.divider {
background-color: #E1DFDD;
min-height: 1px;
max-height: 1px;
}
.secondary-text {
color: #605E5C;
}
.disabled-text {
color: #A19F9D;
}

BIN
resources/connect.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1 +1 @@
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.25 4a.75.75 0 0 1 .743.648L9 4.75v2.773l1.874 2.815a.75.75 0 0 1 .117.306l.009.11v4.496a.75.75 0 0 1-.649.743L10.25 16H8.996v3.254a.75.75 0 0 1-1.492.101l-.007-.101L7.496 16 5.5 15.999v3.258a.75.75 0 0 1-1.493.101l-.006-.101L4 15.999 2.75 16a.75.75 0 0 1-.742-.648l-.007-.102v-4.496a.75.75 0 0 1 .071-.32l.055-.096 1.874-2.815V4.75a.75.75 0 0 1 1.493-.102l.007.102v3a.75.75 0 0 1-.072.32l-.054.096-1.874 2.815V14.5H9.5v-3.52L7.625 8.167a.75.75 0 0 1-.117-.306L7.5 7.75v-3A.75.75 0 0 1 8.25 4Zm7.004.001h4.496a.75.75 0 0 1 .743.649l.007.101L20.499 8h.75a.75.75 0 0 1 .744.648L22 8.75v4.496c0 .111-.025.22-.072.32l-.054.096L20 16.477v2.773a.75.75 0 0 1-1.494.102l-.006-.102v-3c0-.11.024-.22.071-.32l.054-.096L20.5 13.02V9.5h-5.998v3.52l1.874 2.814a.749.749 0 0 1 .118.306l.008.11v3a.75.75 0 0 1-1.493.102l-.007-.102v-2.773l-1.874-2.815a.75.75 0 0 1-.118-.306l-.008-.11V8.75a.75.75 0 0 1 .648-.743L13.752 8h.752V4.751a.75.75 0 0 1 .649-.743l.101-.007h4.496-4.496ZM19 5.501h-2.996V8h2.995V5.501Z" fill="#000000"/></svg>
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.25 4a.75.75 0 0 1 .743.648L9 4.75v2.773l1.874 2.815a.75.75 0 0 1 .117.306l.009.11v4.496a.75.75 0 0 1-.649.743L10.25 16H8.996v3.254a.75.75 0 0 1-1.492.101l-.007-.101L7.496 16 5.5 15.999v3.258a.75.75 0 0 1-1.493.101l-.006-.101L4 15.999 2.75 16a.75.75 0 0 1-.742-.648l-.007-.102v-4.496a.75.75 0 0 1 .071-.32l.055-.096 1.874-2.815V4.75a.75.75 0 0 1 1.493-.102l.007.102v3a.75.75 0 0 1-.072.32l-.054.096-1.874 2.815V14.5H9.5v-3.52L7.625 8.167a.75.75 0 0 1-.117-.306L7.5 7.75v-3A.75.75 0 0 1 8.25 4Zm7.004.001h4.496a.75.75 0 0 1 .743.649l.007.101L20.499 8h.75a.75.75 0 0 1 .744.648L22 8.75v4.496c0 .111-.025.22-.072.32l-.054.096L20 16.477v2.773a.75.75 0 0 1-1.494.102l-.006-.102v-3c0-.11.024-.22.071-.32l.054-.096L20.5 13.02V9.5h-5.998v3.52l1.874 2.814a.749.749 0 0 1 .118.306l.008.11v3a.75.75 0 0 1-1.493.102l-.007-.102v-2.773l-1.874-2.815a.75.75 0 0 1-.118-.306l-.008-.11V8.75a.75.75 0 0 1 .648-.743L13.752 8h.752V4.751a.75.75 0 0 1 .649-.743l.101-.007h4.496-4.496ZM19 5.501h-2.996V8h2.995V5.501Z" fill="#888888"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1 +0,0 @@
<svg width="48" height="48" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.25 4a.75.75 0 0 1 .743.648L9 4.75v2.773l1.874 2.815a.75.75 0 0 1 .117.306l.009.11v4.496a.75.75 0 0 1-.649.743L10.25 16H8.996v3.254a.75.75 0 0 1-1.492.101l-.007-.101L7.496 16 5.5 15.999v3.258a.75.75 0 0 1-1.493.101l-.006-.101L4 15.999 2.75 16a.75.75 0 0 1-.742-.648l-.007-.102v-4.496a.75.75 0 0 1 .071-.32l.055-.096 1.874-2.815V4.75a.75.75 0 0 1 1.493-.102l.007.102v3a.75.75 0 0 1-.072.32l-.054.096-1.874 2.815V14.5H9.5v-3.52L7.625 8.167a.75.75 0 0 1-.117-.306L7.5 7.75v-3A.75.75 0 0 1 8.25 4Zm7.004.001h4.496a.75.75 0 0 1 .743.649l.007.101L20.499 8h.75a.75.75 0 0 1 .744.648L22 8.75v4.496c0 .111-.025.22-.072.32l-.054.096L20 16.477v2.773a.75.75 0 0 1-1.494.102l-.006-.102v-3c0-.11.024-.22.071-.32l.054-.096L20.5 13.02V9.5h-5.998v3.52l1.874 2.814a.749.749 0 0 1 .118.306l.008.11v3a.75.75 0 0 1-1.493.102l-.007-.102v-2.773l-1.874-2.815a.75.75 0 0 1-.118-.306l-.008-.11V8.75a.75.75 0 0 1 .648-.743L13.752 8h.752V4.751a.75.75 0 0 1 .649-.743l.101-.007h4.496-4.496ZM19 5.501h-2.996V8h2.995V5.501Z" fill="#DDE6E8"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
resources/exit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
resources/exit_red.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
resources/sync.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1 +1 @@
<svg width="48" height="48" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M16 8.25a.75.75 0 0 1 1.5 0v3.25a.75.75 0 0 1-.75.75H14a.75.75 0 0 1 0-1.5h1.27A3.502 3.502 0 0 0 12 8.5c-1.093 0-2.037.464-2.673 1.23a.75.75 0 1 1-1.154-.96C9.096 7.66 10.463 7 12 7c1.636 0 3.088.785 4 2v-.75ZM8 15v.75a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 .75-.75H10a.75.75 0 0 1 0 1.5H8.837a3.513 3.513 0 0 0 5.842.765.75.75 0 1 1 1.142.972A5.013 5.013 0 0 1 8 15Zm4-13C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2Zm8.5 10a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0Z" fill="#000000"/></svg>
<svg width="48" height="48" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M16 8.25a.75.75 0 0 1 1.5 0v3.25a.75.75 0 0 1-.75.75H14a.75.75 0 0 1 0-1.5h1.27A3.502 3.502 0 0 0 12 8.5c-1.093 0-2.037.464-2.673 1.23a.75.75 0 1 1-1.154-.96C9.096 7.66 10.463 7 12 7c1.636 0 3.088.785 4 2v-.75ZM8 15v.75a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 .75-.75H10a.75.75 0 0 1 0 1.5H8.837a3.513 3.513 0 0 0 5.842.765.75.75 0 1 1 1.142.972A5.013 5.013 0 0 1 8 15Zm4-13C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2Zm8.5 10a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0Z" fill="#888888"/></svg>

Before

Width:  |  Height:  |  Size: 609 B

After

Width:  |  Height:  |  Size: 609 B

BIN
resources/sync_green.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
resources/sync_yellow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
resources/update.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

1
resources/update.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.747 4h3.464a.75.75 0 0 1 .102 1.493l-.102.007H6.747a2.25 2.25 0 0 0-2.245 2.096l-.005.154v9.5a2.25 2.25 0 0 0 2.096 2.245l.154.005h9.5a2.25 2.25 0 0 0 2.245-2.096l.005-.154v-.498a.75.75 0 0 1 1.494-.101l.006.101v.498a3.75 3.75 0 0 1-3.55 3.745l-.2.005h-9.5a3.75 3.75 0 0 1-3.745-3.55l-.005-.2v-9.5a3.75 3.75 0 0 1 3.55-3.745l.2-.005h3.464-3.464ZM14.5 6.544V3.75a.75.75 0 0 1 1.187-.61l.082.069 5.994 5.75c.28.268.306.7.077.997l-.077.085-5.994 5.752a.75.75 0 0 1-1.262-.434l-.007-.107V12.45l-.321-.006c-2.658-.008-4.93 1.083-6.865 3.301-.496.568-1.425.132-1.306-.612.827-5.14 3.6-8.045 8.19-8.559l.302-.03V3.75v2.794Z" fill="#888888"/></svg>

After

Width:  |  Height:  |  Size: 747 B