1 Commits

257 changed files with 3439 additions and 4086 deletions

6
.gitignore vendored
View File

@@ -1,6 +0,0 @@
debug.log
.venv
.venv.bak
.idea
.vscode
__pycache__

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.12 (wire-py)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (wire-py)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/wire-py.iml" filepath="$PROJECT_DIR$/.idea/wire-py.iml" />
</modules>
</component>
</project>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
<changelist name="Uncommitted_changes_before_Checkout_at_19_08_24,_06_49_[Changes]" date="1724042999949" recycled="false" toDelete="true">
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/Uncommitted_changes_before_Checkout_at_19_08_24,_06_49_[Changes]/shelved.patch" />
<option name="DESCRIPTION" value="Uncommitted changes before Checkout at 19.08.24, 06:49 [Changes]" />
</changelist>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

10
.idea/wire-py.iml generated Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

606
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,606 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="940e1630-c825-4d4c-be80-bc11f543c122" name="Changes" comment=" - Update Translate Files">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cls_mth_fc.py" beforeDir="false" afterPath="$PROJECT_DIR$/cls_mth_fc.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Python Script" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$" value="1.11.0824" />
</map>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
<option name="UPDATE_TYPE" value="REBASE" />
</component>
<component name="HighlightingSettingsPerFile">
<setting file="file://$PROJECT_DIR$/wg_func.py" root0="SKIP_INSPECTION" />
<setting file="file://$PROJECT_DIR$/wg_main.py" root0="FORCE_HIGHLIGHTING" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 3
}</component>
<component name="ProjectId" id="2kSbZdjOvr0wsVJSNcaMwSfVaxR" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;ASKED_ADD_EXTERNAL_FILES&quot;: &quot;true&quot;,
&quot;Python.INSTALL.executor&quot;: &quot;Run&quot;,
&quot;Python.install.executor&quot;: &quot;Run&quot;,
&quot;Python.main.executor&quot;: &quot;Run&quot;,
&quot;Python.messagebox.executor&quot;: &quot;Run&quot;,
&quot;Python.start_wg.executor&quot;: &quot;Run&quot;,
&quot;Python.testtheme.executor&quot;: &quot;Run&quot;,
&quot;Python.wg_func.executor&quot;: &quot;Run&quot;,
&quot;Python.wg_main.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;Shell Script.install.executor&quot;: &quot;Run&quot;,
&quot;Shell Script.run_as.executor&quot;: &quot;Run&quot;,
&quot;git-widget-placeholder&quot;: &quot;1.11.1024&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/punix/Pyapps/wire-py&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;reference.settingsdialog.IDE.editor.colors&quot;
}
}</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/lx-icons" />
<recent name="$PROJECT_DIR$" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/TK-Themes/theme" />
<recent name="$PROJECT_DIR$/TK-Themes" />
<recent name="$PROJECT_DIR$" />
<recent name="$PROJECT_DIR$/wire-py" />
</key>
</component>
<component name="RunManager" selected="Python.wg_main">
<configuration name="start_wg" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="wire-py" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/start_wg.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="wg_main" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
<module name="wire-py" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/wg_main.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Python.start_wg" />
</list>
</recent_temporary>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-python-sdk-8336bb23522e-31b6be0877a2-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-241.19072.16" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="940e1630-c825-4d4c-be80-bc11f543c122" name="Changes" comment="" />
<created>1723279982210</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1723279982210</updated>
</task>
<task id="LOCAL-00029" summary="little fixes a labels when stop and start, installer first functions works">
<option name="closed" value="true" />
<created>1725991610908</created>
<option name="number" value="00029" />
<option name="presentableId" value="LOCAL-00029" />
<option name="project" value="LOCAL" />
<updated>1725991610908</updated>
</task>
<task id="LOCAL-00030" summary="little fixes, add msg_window() &#10;function for Messagebox to show a tk.Toplevel()&#10;replace var = open() with: &#10;with open() as var:&#10;and remove by classes (tk.tk) and super()">
<option name="closed" value="true" />
<created>1726349168248</created>
<option name="number" value="00030" />
<option name="presentableId" value="LOCAL-00030" />
<option name="project" value="LOCAL" />
<updated>1726349168248</updated>
</task>
<task id="LOCAL-00031" summary="in delete replace open with Path&#10;install fixes">
<option name="closed" value="true" />
<created>1726359012150</created>
<option name="number" value="00031" />
<option name="presentableId" value="LOCAL-00031" />
<option name="project" value="LOCAL" />
<updated>1726359012150</updated>
</task>
<task id="LOCAL-00032" summary="new format little fixes icons sort add policy add .desktop File install Part 2">
<option name="closed" value="true" />
<created>1726599446537</created>
<option name="number" value="00032" />
<option name="presentableId" value="LOCAL-00032" />
<option name="project" value="LOCAL" />
<updated>1726599446538</updated>
</task>
<task id="LOCAL-00033" summary="install fix for set dir right">
<option name="closed" value="true" />
<created>1726599588155</created>
<option name="number" value="00033" />
<option name="presentableId" value="LOCAL-00033" />
<option name="project" value="LOCAL" />
<updated>1726599588155</updated>
</task>
<task id="LOCAL-00034" summary="fix checkbox disable and policy rename main.py to wg_main.py">
<option name="closed" value="true" />
<created>1726650691719</created>
<option name="number" value="00034" />
<option name="presentableId" value="LOCAL-00034" />
<option name="project" value="LOCAL" />
<updated>1726650691719</updated>
</task>
<task id="LOCAL-00035" summary="fix rename in Messagebox warning to error">
<option name="closed" value="true" />
<created>1726652747322</created>
<option name="number" value="00035" />
<option name="presentableId" value="LOCAL-00035" />
<option name="project" value="LOCAL" />
<updated>1726652747322</updated>
</task>
<task id="LOCAL-00036" summary="set rights in install and a .conf a import Tunnel Filedialog Part 1 /home when open">
<option name="closed" value="true" />
<created>1726691611936</created>
<option name="number" value="00036" />
<option name="presentableId" value="LOCAL-00036" />
<option name="project" value="LOCAL" />
<updated>1726691611936</updated>
</task>
<task id="LOCAL-00037" summary="fix set rights in install and a .conf a import Tunnel Filedialog Part 1 /home when open">
<option name="closed" value="true" />
<created>1726734843529</created>
<option name="number" value="00037" />
<option name="presentableId" value="LOCAL-00037" />
<option name="project" value="LOCAL" />
<updated>1726734843529</updated>
</task>
<task id="LOCAL-00038" summary="fix a filedialog for hidden Files work&#10;install rollback to bash for start wirepy and wirepy rollback to bash">
<option name="closed" value="true" />
<created>1726764877546</created>
<option name="number" value="00038" />
<option name="presentableId" value="LOCAL-00038" />
<option name="project" value="LOCAL" />
<updated>1726764877546</updated>
</task>
<task id="LOCAL-00039" summary="install rollback bash to py wirepy and wirepy rollback to py">
<option name="closed" value="true" />
<created>1726770649542</created>
<option name="number" value="00039" />
<option name="presentableId" value="LOCAL-00039" />
<option name="project" value="LOCAL" />
<updated>1726770649542</updated>
</task>
<task id="LOCAL-00040" summary="fix install and .desktop File Tar works now for user home and filebrowser.askfilebrowser start now in user home">
<option name="closed" value="true" />
<created>1726777434040</created>
<option name="number" value="00040" />
<option name="presentableId" value="LOCAL-00040" />
<option name="project" value="LOCAL" />
<updated>1726777434040</updated>
</task>
<task id="LOCAL-00041" summary="replace tar with zip and Check if Zip file is empty">
<option name="closed" value="true" />
<created>1726836930251</created>
<option name="number" value="00041" />
<option name="presentableId" value="LOCAL-00041" />
<option name="project" value="LOCAL" />
<updated>1726836930251</updated>
</task>
<task id="LOCAL-00042" summary="Create your own message boxes for export">
<option name="closed" value="true" />
<created>1726841190285</created>
<option name="number" value="00042" />
<option name="presentableId" value="LOCAL-00042" />
<option name="project" value="LOCAL" />
<updated>1726841190285</updated>
</task>
<task id="LOCAL-00043" summary="chown Export File to 1000:1000">
<option name="closed" value="true" />
<created>1726860371820</created>
<option name="number" value="00043" />
<option name="presentableId" value="LOCAL-00043" />
<option name="project" value="LOCAL" />
<updated>1726860371820</updated>
</task>
<task id="LOCAL-00044" summary="add rename Label rename works">
<option name="closed" value="true" />
<created>1726915238475</created>
<option name="number" value="00044" />
<option name="presentableId" value="LOCAL-00044" />
<option name="project" value="LOCAL" />
<updated>1726915238475</updated>
</task>
<task id="LOCAL-00045" summary="add con_to_dict in import for write PreSharedKey in .key File to warning if tunnel has already been imported and delete that the key is deleted again">
<option name="closed" value="true" />
<created>1726959423800</created>
<option name="number" value="00045" />
<option name="presentableId" value="LOCAL-00045" />
<option name="project" value="LOCAL" />
<updated>1726959423800</updated>
</task>
<task id="LOCAL-00046" summary="add con_to_dict in import for write PreSharedKey in .key File to warning if tunnel has already been imported and delete that the key is deleted again&#10;now works">
<option name="closed" value="true" />
<created>1727015078922</created>
<option name="number" value="00046" />
<option name="presentableId" value="LOCAL-00046" />
<option name="project" value="LOCAL" />
<updated>1727015078922</updated>
</task>
<task id="LOCAL-00047" summary="Descriptions added in wg_func-py">
<option name="closed" value="true" />
<created>1727018233930</created>
<option name="number" value="00047" />
<option name="presentableId" value="LOCAL-00047" />
<option name="project" value="LOCAL" />
<updated>1727018233930</updated>
</task>
<task id="LOCAL-00048" summary="If tunnel is renamed and this is in the car start,&#10;is now renamed the label">
<option name="closed" value="true" />
<created>1727028762875</created>
<option name="number" value="00048" />
<option name="presentableId" value="LOCAL-00048" />
<option name="project" value="LOCAL" />
<updated>1727028762875</updated>
</task>
<task id="LOCAL-00049" summary="fix scrollbar view with set self.y_height = 330 to self.y_height = 340">
<option name="closed" value="true" />
<created>1727028915701</created>
<option name="number" value="00049" />
<option name="presentableId" value="LOCAL-00049" />
<option name="project" value="LOCAL" />
<updated>1727028915701</updated>
</task>
<task id="LOCAL-00050" summary="in msg_window two further parameters to be added to the pass, so height and wide can also be specified.&#10;In rename, messages come now if new names do not fit&#10;Fix Index Error on msg_window()">
<option name="closed" value="true" />
<created>1727118598759</created>
<option name="number" value="00050" />
<option name="presentableId" value="LOCAL-00050" />
<option name="project" value="LOCAL" />
<updated>1727118598760</updated>
</task>
<task id="LOCAL-00051" summary="ad max 12-character message, no character message and special_characters message for entry label">
<option name="closed" value="true" />
<created>1727288788988</created>
<option name="number" value="00051" />
<option name="presentableId" value="LOCAL-00051" />
<option name="project" value="LOCAL" />
<updated>1727288788988</updated>
</task>
<task id="LOCAL-00052" summary="info icon shadow fix end msg Export fix to">
<option name="closed" value="true" />
<created>1727347126769</created>
<option name="number" value="00052" />
<option name="presentableId" value="LOCAL-00052" />
<option name="project" value="LOCAL" />
<updated>1727347126769</updated>
</task>
<task id="LOCAL-00053" summary="little fixes">
<option name="closed" value="true" />
<created>1727378355274</created>
<option name="number" value="00053" />
<option name="presentableId" value="LOCAL-00053" />
<option name="project" value="LOCAL" />
<updated>1727378355275</updated>
</task>
<task id="LOCAL-00054" summary="fix msg_boxes when tunnel list = 0 a Start, Delete and Export">
<option name="closed" value="true" />
<created>1727379755537</created>
<option name="number" value="00054" />
<option name="presentableId" value="LOCAL-00054" />
<option name="project" value="LOCAL" />
<updated>1727379755537</updated>
</task>
<task id="LOCAL-00055" summary="fix installer add keys file">
<option name="closed" value="true" />
<created>1727380793216</created>
<option name="number" value="00055" />
<option name="presentableId" value="LOCAL-00055" />
<option name="project" value="LOCAL" />
<updated>1727380793216</updated>
</task>
<task id="LOCAL-00056" summary="Changelog create When exporting, the folder is now copied to /tmp and the non .conf files are deleted before the zip file is created. In main.py os import removed. Since os have been replaced by pathlib and shutil.&#10;Start with version number 1.4.7&#10;Message window size corrected so text is displayed better">
<option name="closed" value="true" />
<created>1727525609727</created>
<option name="number" value="00056" />
<option name="presentableId" value="LOCAL-00056" />
<option name="project" value="LOCAL" />
<updated>1727525609728</updated>
</task>
<task id="LOCAL-00057" summary="Fix msg_window and remove x , y argument&#10;Install further adapted and with colored&#10;text if user is not in group sudo or wheel.&#10;Added to install Opensuse for installation">
<option name="closed" value="true" />
<created>1728059870005</created>
<option name="number" value="00057" />
<option name="presentableId" value="LOCAL-00057" />
<option name="project" value="LOCAL" />
<updated>1728059870005</updated>
</task>
<task id="LOCAL-00058" summary=" - Menu add &#10; - New Modern Dark and Light(default) Theme">
<option name="closed" value="true" />
<created>1729103964804</created>
<option name="number" value="00058" />
<option name="presentableId" value="LOCAL-00058" />
<option name="project" value="LOCAL" />
<updated>1729103964804</updated>
</task>
<task id="LOCAL-00059" summary=" - Theme modify to water-theme&#10; - add ttk.Menubutton vor modern Menu and automatic theme and textvariable for color on font in menu">
<option name="closed" value="true" />
<created>1729283656386</created>
<option name="number" value="00059" />
<option name="presentableId" value="LOCAL-00059" />
<option name="project" value="LOCAL" />
<updated>1729283656387</updated>
</task>
<task id="LOCAL-00060" summary=" - Theme modify to water-theme&#10; - add ttk.Menubutton vor modern Menu and automatic theme and textvariable for color on font in menu">
<option name="closed" value="true" />
<created>1729283719951</created>
<option name="number" value="00060" />
<option name="presentableId" value="LOCAL-00060" />
<option name="project" value="LOCAL" />
<updated>1729283719951</updated>
</task>
<task id="LOCAL-00061" summary=" - Add Options, Help, Update Label and Update Menubutton &#10; - Theme now separate Light and Dark">
<option name="closed" value="true" />
<created>1729353898829</created>
<option name="number" value="00061" />
<option name="presentableId" value="LOCAL-00061" />
<option name="project" value="LOCAL" />
<updated>1729353898830</updated>
</task>
<task id="LOCAL-00062" summary="- Optimize Class. Move to wg_main Import Start/StopBTN and Tooltip">
<option name="closed" value="true" />
<created>1729541504291</created>
<option name="number" value="00062" />
<option name="presentableId" value="LOCAL-00062" />
<option name="project" value="LOCAL" />
<updated>1729541504292</updated>
</task>
<task id="LOCAL-00063" summary="- Optimize Class. Move to wg_main Import Start/StopBTN and Tooltip">
<option name="closed" value="true" />
<created>1729541561434</created>
<option name="number" value="00063" />
<option name="presentableId" value="LOCAL-00063" />
<option name="project" value="LOCAL" />
<updated>1729541561434</updated>
</task>
<task id="LOCAL-00064" summary="- Optimize Class. Move to wg_main Import Start/StopBTN and Tooltip">
<option name="closed" value="true" />
<created>1729593628907</created>
<option name="number" value="00064" />
<option name="presentableId" value="LOCAL-00064" />
<option name="project" value="LOCAL" />
<updated>1729593628908</updated>
</task>
<task id="LOCAL-00065" summary="- Optimize Class and Tooltip">
<option name="closed" value="true" />
<created>1729938941026</created>
<option name="number" value="00065" />
<option name="presentableId" value="LOCAL-00065" />
<option name="project" value="LOCAL" />
<updated>1729938941027</updated>
</task>
<task id="LOCAL-00066" summary="- methods from class MainWindow move to class FrameWidgets for active color_label when theme change&#10;- optimize columnconfigure, rowconfigure in class MainWindow and FrameWidgets&#10;- add new Frame for Widgets on Bottom&#10;- optimize from tkinter * to from tkinter import filedialog, ttk, TclError">
<option name="closed" value="true" />
<created>1731097309468</created>
<option name="number" value="00066" />
<option name="presentableId" value="LOCAL-00066" />
<option name="project" value="LOCAL" />
<updated>1731097309468</updated>
</task>
<task id="LOCAL-00067" summary="- methods from class MainWindow move to class FrameWidgets for active color_label when theme change&#10;- optimize columnconfigure, rowconfigure in class MainWindow and FrameWidgets&#10;- add new Frame for Widgets on Bottom&#10;- optimize from tkinter * to from tkinter import filedialog, ttk, TclError">
<option name="closed" value="true" />
<created>1731097969343</created>
<option name="number" value="00067" />
<option name="presentableId" value="LOCAL-00067" />
<option name="project" value="LOCAL" />
<updated>1731097969344</updated>
</task>
<task id="LOCAL-00068" summary="- methods from class MainWindow move to class FrameWidgets for active color_label when theme change&#10;- optimize columnconfigure, rowconfigure in class MainWindow and FrameWidgets&#10;- add new Frame for Widgets on Bottom&#10;- optimize from tkinter * to from tkinter import filedialog, ttk, TclError">
<option name="closed" value="true" />
<created>1731098372497</created>
<option name="number" value="00068" />
<option name="presentableId" value="LOCAL-00068" />
<option name="project" value="LOCAL" />
<updated>1731098372497</updated>
</task>
<task id="LOCAL-00069" summary=" - - Fix Checkbutton Autostart when first install Wire-Py">
<option name="closed" value="true" />
<created>1731690583059</created>
<option name="number" value="00069" />
<option name="presentableId" value="LOCAL-00069" />
<option name="project" value="LOCAL" />
<updated>1731690583060</updated>
</task>
<task id="LOCAL-00070" summary=" - - Fix Checkbutton Autostart when first install Wire-Py">
<option name="closed" value="true" />
<created>1731836942211</created>
<option name="number" value="00070" />
<option name="presentableId" value="LOCAL-00070" />
<option name="project" value="LOCAL" />
<updated>1731836942212</updated>
</task>
<task id="LOCAL-00071" summary=" - Update Translate Files">
<option name="closed" value="true" />
<created>1731840048762</created>
<option name="number" value="00071" />
<option name="presentableId" value="LOCAL-00071" />
<option name="project" value="LOCAL" />
<updated>1731840048763</updated>
</task>
<task id="LOCAL-00072" summary=" - Update Translate Files">
<option name="closed" value="true" />
<created>1731840089956</created>
<option name="number" value="00072" />
<option name="presentableId" value="LOCAL-00072" />
<option name="project" value="LOCAL" />
<updated>1731840089956</updated>
</task>
<task id="LOCAL-00073" summary=" - Update Translate Files">
<option name="closed" value="true" />
<created>1731840188277</created>
<option name="number" value="00073" />
<option name="presentableId" value="LOCAL-00073" />
<option name="project" value="LOCAL" />
<updated>1731840188278</updated>
</task>
<task id="LOCAL-00074" summary=" - Update Translate Files">
<option name="closed" value="true" />
<created>1731840383592</created>
<option name="number" value="00074" />
<option name="presentableId" value="LOCAL-00074" />
<option name="project" value="LOCAL" />
<updated>1731840383592</updated>
</task>
<task id="LOCAL-00075" summary=" - Update Translate Files">
<option name="closed" value="true" />
<created>1731841930614</created>
<option name="number" value="00075" />
<option name="presentableId" value="LOCAL-00075" />
<option name="project" value="LOCAL" />
<updated>1731841930615</updated>
</task>
<task id="LOCAL-00076" summary=" - Update Translate Files">
<option name="closed" value="true" />
<created>1731844213239</created>
<option name="number" value="00076" />
<option name="presentableId" value="LOCAL-00076" />
<option name="project" value="LOCAL" />
<updated>1731844213239</updated>
</task>
<task id="LOCAL-00077" summary=" - Update Translate Files">
<option name="closed" value="true" />
<created>1731844339039</created>
<option name="number" value="00077" />
<option name="presentableId" value="LOCAL-00077" />
<option name="project" value="LOCAL" />
<updated>1731844339039</updated>
</task>
<option name="localTasksCounter" value="78" />
<servers />
</component>
<component name="UnknownFeatures">
<option featureType="com.intellij.fileTypeFactory" implementationName="*.policy" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State>
<option name="FILTERS">
<map>
<entry key="branch">
<value>
<list>
<option value="1.11.0824" />
</list>
</value>
</entry>
</map>
</option>
</State>
</value>
</entry>
</map>
</option>
</component>
<component name="VcsManagerConfiguration">
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
<MESSAGE value="replace tar with zip and Check if Zip file is empty" />
<MESSAGE value="Create your own message boxes for export" />
<MESSAGE value="chown Export File to 1000:1000" />
<MESSAGE value="add rename Label rename works" />
<MESSAGE value="add con_to_dict in import for write PreSharedKey in .key File to warning if tunnel has already been imported and delete that the key is deleted again" />
<MESSAGE value="add con_to_dict in import for write PreSharedKey in .key File to warning if tunnel has already been imported and delete that the key is deleted again&#10;now works" />
<MESSAGE value="Descriptions added in wg_func-py" />
<MESSAGE value="If tunnel is renamed and this is in the car start,&#10;is now renamed the label" />
<MESSAGE value="fix scrollbar view with set self.y_height = 330 to self.y_height = 340" />
<MESSAGE value="in msg_window two further parameters to be added to the pass, so height and wide can also be specified.&#10;In rename, messages come now if new names do not fit&#10;Fix Index Error on msg_window()" />
<MESSAGE value="ad max 12-character message, no character message and special_characters message for entry label" />
<MESSAGE value="info icon shadow fix end msg Export fix to" />
<MESSAGE value="little fixes" />
<MESSAGE value="fix msg_boxes when tunnel list = 0 a Start, Delete and Export" />
<MESSAGE value="fix installer add keys file" />
<MESSAGE value="Changelog create When exporting, the folder is now copied to /tmp and the non .conf files are deleted before the zip file is created. In main.py os import removed. Since os have been replaced by pathlib and shutil.&#10;Start with version number 1.4.7&#10;Message window size corrected so text is displayed better" />
<MESSAGE value="Fix msg_window and remove x , y argument&#10;Install further adapted and with colored&#10;text if user is not in group sudo or wheel.&#10;Added to install Opensuse for installation" />
<MESSAGE value=" - Menu add &#10; - New Modern Dark and Light(default) Theme" />
<MESSAGE value=" - Theme modify to water-theme&#10; - add ttk.Menubutton vor modern Menu and automatic theme and textvariable for color on font in menu" />
<MESSAGE value=" - Add Options, Help, Update Label and Update Menubutton &#10; - Theme now separate Light and Dark" />
<MESSAGE value="- Optimize Class. Move to wg_main Import Start/StopBTN and Tooltip" />
<MESSAGE value="- Optimize Class and Tooltip" />
<MESSAGE value="- methods from class MainWindow move to class FrameWidgets for active color_label when theme change&#10;- optimize columnconfigure, rowconfigure in class MainWindow and FrameWidgets&#10;- add new Frame for Widgets on Bottom&#10;- optimize from tkinter * to from tkinter import filedialog, ttk, TclError" />
<MESSAGE value=" - - Fix Checkbutton Autostart when first install Wire-Py" />
<MESSAGE value=" - Update Translate Files" />
<option name="LAST_COMMIT_MESSAGE" value=" - Update Translate Files" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/wg_main.py</url>
<line>1128</line>
<option name="timeStamp" value="3" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project>

108
Changelog
View File

@@ -3,129 +3,27 @@ My standard System: Linux Mint 22 Cinnamon
## [Unreleased]
- os import in common_tools.py replaced by other methods
- os import in cls_mth_fc.py replaced by other methods
- If Wire-Py already runs, prevent further start
- for loops with lists replaced by List Comprehensions
### Added
10.08.2025
- Wirepy divided into several scripts for better overview and maintenance
- Own file dialogue is now used to open Wireguard files for the import.
- New update notification. A canvas ring flashes when updates are available
- UI size change now grow all widgets with.
- Update Search at start of the app now starts in a thread to not stop the GUI
- Logviewer replaces with new button in the menu bar,
a new window opens in which the necessary infos stand when errors occur.
### Added
01.08.2025
- Replace Imagemanager with IconManager and remove ImageManager from wp_app_config.py
- Logging improved
### Added
02-07-2025
- Complete code for faulty f" string configuration dur addicted and fixed
- updater replace Downloadbutton with Lx Tools installer
- remove logviewer icon
- add settings.png icon for update button
### Added
27-06-2025
- Header added for more modern desing
- Sizes adjust the frames and labels improve
- More modern desing for listbox, Address, Dns and Endpoint
- ui works now better with rename button
- add Image class for manage Images
### Added
23-06-2025
- all msg_window with MassageDialog replaced
### Added
14-06-2025
- replace msg_window with MassageDialog
- vpn stop icon corrected
### Added
07-06-2025
- ssl_decrypt now directly checks whether encrypted files are located in the specified directory.
- This method has been removed from common_tools. The path has been adjusted ssl_decrypt likely
had an incorrect path. No files were decrypted. This has been fixed.
- Key generation has been removed from ssl_decrypt, as it is only needed in ssl_encrypt.
- Logviewer now in the settings menu. Moduls now as libs in share_libs.
- The Lx Tools installer is there to ensure that everything required for the selected app is installed.
### Added
13-04-20255
- Installer update for Open Suse Tumbleweed and Leap
- add symbolic link wirepy.py
### Added
09-04-2025
- Installer now with query and remove
- Icons merged
### Added
07-04-2025
- Installers will support other systems again
- Installer is now finished clean with wrong password
- Rename wg_main to wirepy
### Added
03-03-2025
- Fixes a new user files create
- Crypt and Decrypt Config Files in ~/.config/wire_py
### Added
02-03-2025
- Fix ipv6 in Config File on import
- Wirepy run now as user
- settings, AppConfig.KEYS_FILE and Config Files now in ~/.config/wire_py
- settings, keys and Config Files now in ~/.config/wire_py
- For new users, the required files are created and autostart service is started.
- Tunnels are now read from the directory to view them in the list.
To display only own tunnels, and read errors are minimized.
### Added
10-11-2024
- Fix Checkbutton Autostart when first install Wire-Py
- Update Translate Files
### Added
10-11-2024

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

View File

@@ -1,10 +1,5 @@
# Copyright © 2021 rdbende <rdbende@gmail.com>
# inspired by rdbende modified azure to water by Désire Werner Menrath polunga40@unity-mail.de 2024
# Update add New Style TButton.Borderless for Buttons without edge and without rounded corners by
# Désire Werner Menrath 28-07-2025
# Add Update Progressbars Round ends on Horizontal and Vertikal + add small bars
# Désire Werner Menrath 30-07-2025
source [file join [file dirname [info script]] theme light.tcl]
source [file join [file dirname [info script]] theme dark.tcl]
@@ -18,7 +13,7 @@ proc set_theme {mode} {
array set colors {
-fg "#ffffff"
-bg "#333333"
-disabledfg "#aaaaaa"
-disabledfg "#ffffff"
-disabledbg "#737373"
-selectfg "#000000"
-selectbg "#00c4ff"
@@ -58,7 +53,7 @@ proc set_theme {mode} {
-fg "#000000"
-bg "#ffffff"
-disabledfg "#737373"
-disabledbg "#f0f0f0"
-disabledbg "#ffffff"
-selectfg "#000000"
-selectbg "#00c4ff"
}

4
Wire-Py.desktop Normal file → Executable file
View File

@@ -1,7 +1,7 @@
[Desktop Entry]
Type=Application
Name=Wire-Py
Exec=/usr/local/bin/wirepy.py
Exec=/usr/local/bin/wg_main.py
Terminal=false
Categories=Network;
Icon=/usr/share/icons/lx-icons/128/wg_vpn.png
Icon=/usr/share/icons/wp-icons/128/wg_vpn.png

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,970 +0,0 @@
"""
A Tkinter widget for displaying animated icons.
This module provides the AnimatedIcon class, a custom Tkinter Canvas widget
that can display various types of animations. It supports both native Tkinter
drawing and Pillow (PIL) for anti-aliased graphics if available.
"""
import tkinter as tk
from math import sin, cos, pi
from typing import Tuple, Optional
try:
from PIL import Image, ImageDraw, ImageTk
PIL_AVAILABLE = True
except ImportError:
PIL_AVAILABLE = False
def _hex_to_rgb(hex_color: str) -> Tuple[int, int, int]:
"""Converts a hex color string to an RGB tuple."""
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
class AnimatedIcon(tk.Canvas):
"""A custom Tkinter Canvas widget for displaying animations."""
def __init__(self, master: tk.Misc, width: int = 20, height: int = 20, animation_type: str = "counter_arc", color: str = "#2a6fde", highlight_color: str = "#5195ff", use_pillow: bool = False, bg: Optional[str] = None) -> None:
"""
Initializes the AnimatedIcon widget.
Args:
master: The parent widget.
width (int): The width of the icon.
height (int): The height of the icon.
animation_type (str): The type of animation to display.
Options: "counter_arc", "double_arc", "line", "blink".
color (str): The primary color of the icon.
highlight_color (str): The highlight color of the icon.
use_pillow (bool): Whether to use Pillow for drawing if available.
bg (str): The background color of the canvas.
"""
if bg is None:
try:
bg = master.cget("background")
except tk.TclError:
bg = "#f0f0f0" # Fallback color
super().__init__(master, width=width, height=height, bg=bg, highlightthickness=0)
self.width = width
self.height = height
self.animation_type = animation_type
self.color = color
self.highlight_color = highlight_color
self.use_pillow = use_pillow and PIL_AVAILABLE
self.running = False
self.angle = 0
self.pulse_animation = False
self.color_rgb = _hex_to_rgb(self.color)
self.highlight_color_rgb = _hex_to_rgb(self.highlight_color)
if self.use_pillow:
self.image = Image.new(
"RGBA", (width * 4, height * 4), (0, 0, 0, 0))
self.draw = ImageDraw.Draw(self.image)
self.photo_image = None
def _draw_frame(self) -> None:
"""Draws a single frame of the animation."""
if self.use_pillow:
self._draw_pillow_frame()
else:
self._draw_canvas_frame()
def _draw_canvas_frame(self) -> None:
"""Draws a frame using native Tkinter canvas methods."""
self.delete("all")
if self.pulse_animation:
self._draw_canvas_pulse()
elif self.animation_type == "line":
self._draw_canvas_line()
elif self.animation_type == "double_arc":
self._draw_canvas_double_arc()
elif self.animation_type == "counter_arc":
self._draw_canvas_counter_arc()
elif self.animation_type == "blink":
self._draw_canvas_blink()
def _draw_canvas_pulse(self) -> None:
"""Draws the pulse animation using canvas methods."""
center_x, center_y = self.width / 2, self.height / 2
alpha = (sin(self.angle * 5) + 1) / 2 # Faster pulse
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
pulse_color = f"#{r:02x}{g:02x}{b:02x}"
if self.animation_type == "line":
for i in range(8):
angle = i * (pi / 4)
start_x = center_x + cos(angle) * (self.width * 0.2)
start_y = center_y + sin(angle) * (self.height * 0.2)
end_x = center_x + cos(angle) * (self.width * 0.4)
end_y = center_y + sin(angle) * (self.height * 0.4)
self.create_line(start_x, start_y, end_x,
end_y, fill=pulse_color, width=2)
elif self.animation_type == "double_arc":
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
self.create_arc(bbox, start=0, extent=359.9,
style=tk.ARC, outline=pulse_color, width=2)
elif self.animation_type == "counter_arc":
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
self.create_arc(bbox_outer, start=0, extent=359.9,
style=tk.ARC, outline=pulse_color, width=2)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
self.create_arc(bbox_inner, start=0, extent=359.9,
style=tk.ARC, outline=self.color, width=2)
def _draw_canvas_line(self) -> None:
"""Draws the line animation using canvas methods."""
center_x, center_y = self.width / 2, self.height / 2
for i in range(8):
angle = self.angle + i * (pi / 4)
start_x = center_x + cos(angle) * (self.width * 0.2)
start_y = center_y + sin(angle) * (self.height * 0.2)
end_x = center_x + cos(angle) * (self.width * 0.4)
end_y = center_y + sin(angle) * (self.height * 0.4)
alpha = (cos(self.angle * 2 + i * (pi / 4)) + 1) / 2
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
color = f"#{r:02x}{g:02x}{b:02x}"
self.create_line(start_x, start_y, end_x,
end_y, fill=color, width=2)
def _draw_canvas_double_arc(self) -> None:
"""Draws the double arc animation using canvas methods."""
center_x, center_y = self.width / 2, self.height / 2
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
start_angle1 = -self.angle * 180 / pi
extent1 = 120 + 60 * sin(-self.angle)
self.create_arc(bbox, start=start_angle1, extent=extent1,
style=tk.ARC, outline=self.highlight_color, width=2)
start_angle2 = (-self.angle + pi) * 180 / pi
extent2 = 120 + 60 * sin(-self.angle + pi / 2)
self.create_arc(bbox, start=start_angle2, extent=extent2,
style=tk.ARC, outline=self.color, width=2)
def _draw_canvas_counter_arc(self) -> None:
"""Draws the counter arc animation using canvas methods."""
center_x, center_y = self.width / 2, self.height / 2
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
start_angle1 = -self.angle * 180 / pi
self.create_arc(bbox_outer, start=start_angle1, extent=150,
style=tk.ARC, outline=self.highlight_color, width=2)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
start_angle2 = self.angle * 180 / pi + 60
self.create_arc(bbox_inner, start=start_angle2, extent=150,
style=tk.ARC, outline=self.color, width=2)
def _draw_canvas_blink(self) -> None:
"""Draws the blink animation using canvas methods."""
center_x, center_y = self.width / 2, self.height / 2
radius = min(center_x, center_y) * 0.8
alpha = (sin(self.angle * 2) + 1) / 2 # Slower blinking speed
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
blink_color = f"#{r:02x}{g:02x}{b:02x}"
self.create_arc(center_x - radius, center_y - radius, center_x + radius, center_y +
radius, start=0, extent=359.9, style=tk.ARC, outline=blink_color, width=4)
def _draw_pillow_frame(self) -> None:
"""Draws a frame using Pillow for anti-aliased graphics."""
self.draw.rectangle(
[0, 0, self.width * 4, self.height * 4], fill=(0, 0, 0, 0))
if self.pulse_animation:
self._draw_pillow_pulse()
elif self.animation_type == "line":
self._draw_pillow_line()
elif self.animation_type == "double_arc":
self._draw_pillow_double_arc()
elif self.animation_type == "counter_arc":
self._draw_pillow_counter_arc()
elif self.animation_type == "blink":
self._draw_pillow_blink()
resized_image = self.image.resize(
(self.width, self.height), Image.Resampling.LANCZOS)
self.photo_image = ImageTk.PhotoImage(resized_image)
self.delete("all")
self.create_image(0, 0, anchor="nw", image=self.photo_image)
def _draw_pillow_pulse(self) -> None:
"""Draws the pulse animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
alpha = (sin(self.angle * 5) + 1) / 2 # Faster pulse
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
pulse_color = (r, g, b)
if self.animation_type == "line":
for i in range(12):
angle = i * (pi / 6)
start_x = center_x + cos(angle) * (self.width * 0.8)
start_y = center_y + sin(angle) * (self.height * 0.8)
end_x = center_x + cos(angle) * (self.width * 1.6)
end_y = center_y + sin(angle) * (self.height * 1.6)
self.draw.line([(start_x, start_y), (end_x, end_y)],
fill=pulse_color, width=6, joint="curve")
elif self.animation_type == "double_arc":
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
self.draw.arc(bbox, start=0, end=360, fill=pulse_color, width=5)
elif self.animation_type == "counter_arc":
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
self.draw.arc(bbox_outer, start=0, end=360,
fill=pulse_color, width=7)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
self.draw.arc(bbox_inner, start=0, end=360,
fill=self.color_rgb, width=7)
def _draw_pillow_line(self) -> None:
"""Draws the line animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
for i in range(12):
angle = self.angle + i * (pi / 6)
start_x = center_x + cos(angle) * (self.width * 0.8)
start_y = center_y + sin(angle) * (self.height * 0.8)
end_x = center_x + cos(angle) * (self.width * 1.6)
end_y = center_y + sin(angle) * (self.height * 1.6)
alpha = (cos(self.angle * 2.5 + i * (pi / 6)) + 1) / 2
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
color = (r, g, b)
self.draw.line([(start_x, start_y), (end_x, end_y)],
fill=color, width=6, joint="curve")
def _draw_pillow_double_arc(self) -> None:
"""Draws the double arc animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
start_angle1 = self.angle * 180 / pi
extent1 = 120 + 60 * sin(self.angle)
self.draw.arc(bbox, start=start_angle1, end=start_angle1 +
extent1, fill=self.highlight_color_rgb, width=5)
start_angle2 = (self.angle + pi) * 180 / pi
extent2 = 120 + 60 * sin(self.angle + pi / 2)
self.draw.arc(bbox, start=start_angle2, end=start_angle2 +
extent2, fill=self.color_rgb, width=5)
def _draw_pillow_counter_arc(self) -> None:
"""Draws the counter arc animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
start_angle1 = self.angle * 180 / pi
self.draw.arc(bbox_outer, start=start_angle1, end=start_angle1 +
150, fill=self.highlight_color_rgb, width=7)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
start_angle2 = -self.angle * 180 / pi + 60
self.draw.arc(bbox_inner, start=start_angle2,
end=start_angle2 + 150, fill=self.color_rgb, width=7)
def _draw_pillow_blink(self) -> None:
"""Draws the blink animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius = min(center_x, center_y) * 0.8
alpha = (sin(self.angle * 2) + 1) / 2 # Slower blinking speed
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
blink_color = (r, g, b)
self.draw.arc((center_x - radius, center_y - radius, center_x + radius,
center_y + radius), start=0, end=360, fill=blink_color, width=10)
def _draw_stopped_frame(self) -> None:
"""Draws the icon in its stopped (static) state."""
self.delete("all")
if self.use_pillow:
self._draw_pillow_stopped_frame()
else:
self._draw_canvas_stopped_frame()
def _draw_canvas_stopped_frame(self) -> None:
"""Draws the stopped state using canvas methods."""
if self.animation_type == "line":
self._draw_canvas_line_stopped()
elif self.animation_type == "double_arc":
self._draw_canvas_double_arc_stopped()
elif self.animation_type == "counter_arc":
self._draw_canvas_counter_arc_stopped()
elif self.animation_type == "blink":
self._draw_canvas_blink_stopped()
def _draw_canvas_line_stopped(self) -> None:
"""Draws the stopped state for the line animation."""
center_x, center_y = self.width / 2, self.height / 2
for i in range(8):
angle = i * (pi / 4)
start_x = center_x + cos(angle) * (self.width * 0.2)
start_y = center_y + sin(angle) * (self.height * 0.2)
end_x = center_x + cos(angle) * (self.width * 0.4)
end_y = center_y + sin(angle) * (self.height * 0.4)
self.create_line(start_x, start_y, end_x, end_y,
fill=self.highlight_color, width=2)
def _draw_canvas_double_arc_stopped(self) -> None:
"""Draws the stopped state for the double arc animation."""
center_x, center_y = self.width / 2, self.height / 2
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
self.create_arc(bbox, start=0, extent=359.9, style=tk.ARC,
outline=self.highlight_color, width=2)
def _draw_canvas_counter_arc_stopped(self) -> None:
"""Draws the stopped state for the counter arc animation."""
center_x, center_y = self.width / 2, self.height / 2
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
self.create_arc(bbox_outer, start=0, extent=359.9,
style=tk.ARC, outline=self.highlight_color, width=2)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
self.create_arc(bbox_inner, start=0, extent=359.9,
style=tk.ARC, outline=self.color, width=2)
def _draw_canvas_blink_stopped(self) -> None:
"""Draws the stopped state for the blink animation."""
center_x, center_y = self.width / 2, self.height / 2
radius = min(center_x, center_y) * 0.8
self.create_arc(center_x - radius, center_y - radius, center_x + radius, center_y +
radius, start=0, extent=359.9, style=tk.ARC, outline=self.highlight_color, width=4)
def _draw_pillow_stopped_frame(self) -> None:
"""Draws the stopped state using Pillow."""
self.draw.rectangle(
[0, 0, self.width * 4, self.height * 4], fill=(0, 0, 0, 0))
if self.animation_type == "line":
self._draw_pillow_line_stopped()
elif self.animation_type == "double_arc":
self._draw_pillow_double_arc_stopped()
elif self.animation_type == "counter_arc":
self._draw_pillow_counter_arc_stopped()
elif self.animation_type == "blink":
self._draw_pillow_blink_stopped()
resized_image = self.image.resize(
(self.width, self.height), Image.Resampling.LANCZOS)
self.photo_image = ImageTk.PhotoImage(resized_image)
self.create_image(0, 0, anchor="nw", image=self.photo_image)
def _draw_pillow_line_stopped(self) -> None:
"""Draws the stopped state for the line animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
for i in range(12):
angle = i * (pi / 6)
start_x = center_x + cos(angle) * (self.width * 0.8)
start_y = center_y + sin(angle) * (self.height * 0.8)
end_x = center_x + cos(angle) * (self.width * 1.6)
end_y = center_y + sin(angle) * (self.height * 1.6)
self.draw.line([(start_x, start_y), (end_x, end_y)],
fill=self.highlight_color_rgb, width=6, joint="curve")
def _draw_pillow_double_arc_stopped(self) -> None:
"""Draws the stopped state for the double arc animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
self.draw.arc(bbox, start=0, end=360,
fill=self.highlight_color_rgb, width=5)
def _draw_pillow_counter_arc_stopped(self) -> None:
"""Draws the stopped state for the counter arc animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
self.draw.arc(bbox_outer, start=0, end=360,
fill=self.highlight_color_rgb, width=7)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
self.draw.arc(bbox_inner, start=0, end=360,
fill=self.color_rgb, width=7)
def _draw_pillow_blink_stopped(self) -> None:
"""Draws the stopped state for the blink animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius = min(center_x, center_y) * 0.8
self.draw.arc((center_x - radius, center_y - radius, center_x + radius,
center_y + radius), start=0, end=360, fill=self.highlight_color_rgb, width=10)
def _animate(self) -> None:
"""The main animation loop."""
if self.running:
self.angle += 0.1
if self.angle > 2 * pi:
self.angle -= 2 * pi
self._draw_frame()
self.after(30, self._animate)
def start(self, pulse: bool = False) -> None:
"""
Starts the animation.
Args:
pulse (bool): If True, plays a pulsing animation instead of the main one.
"""
if not self.running:
self.pulse_animation = pulse
self.running = True
self._animate()
def stop(self) -> None:
"""Stops the animation and shows the static 'stopped' frame."""
self.running = False
self.pulse_animation = False
self._draw_stopped_frame()
def hide(self) -> None:
"""Stops the animation and clears the canvas."""
self.running = False
self.pulse_animation = False
self.delete("all")
def show_full_circle(self) -> None:
"""Shows the static 'stopped' frame without starting the animation."""
if not self.running:
self._draw_stopped_frame()
try:
from PIL import Image, ImageDraw, ImageTk
PIL_AVAILABLE = True
except ImportError:
PIL_AVAILABLE = False
def _hex_to_rgb(hex_color):
"""Converts a hex color string to an RGB tuple."""
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
class AnimatedIcon(tk.Canvas):
"""A custom Tkinter Canvas widget for displaying animations."""
def __init__(self, master, width=20, height=20, animation_type="counter_arc", color="#2a6fde", highlight_color="#5195ff", use_pillow=False, bg=None):
"""
Initializes the AnimatedIcon widget.
Args:
master: The parent widget.
width (int): The width of the icon.
height (int): The height of the icon.
animation_type (str): The type of animation to display.
Options: "counter_arc", "double_arc", "line", "blink".
color (str): The primary color of the icon.
highlight_color (str): The highlight color of the icon.
use_pillow (bool): Whether to use Pillow for drawing if available.
bg (str): The background color of the canvas.
"""
if bg is None:
try:
bg = master.cget("background")
except tk.TclError:
bg = "#f0f0f0" # Fallback color
super().__init__(master, width=width, height=height, bg=bg, highlightthickness=0)
self.width = width
self.height = height
self.animation_type = animation_type
self.color = color
self.highlight_color = highlight_color
self.use_pillow = use_pillow and PIL_AVAILABLE
self.running = False
self.angle = 0
self.pulse_animation = False
self.color_rgb = _hex_to_rgb(self.color)
self.highlight_color_rgb = _hex_to_rgb(self.highlight_color)
if self.use_pillow:
self.image = Image.new(
"RGBA", (width * 4, height * 4), (0, 0, 0, 0))
self.draw = ImageDraw.Draw(self.image)
self.photo_image = None
def _draw_frame(self):
"""Draws a single frame of the animation."""
if self.use_pillow:
self._draw_pillow_frame()
else:
self._draw_canvas_frame()
def _draw_canvas_frame(self):
"""Draws a frame using native Tkinter canvas methods."""
self.delete("all")
if self.pulse_animation:
self._draw_canvas_pulse()
elif self.animation_type == "line":
self._draw_canvas_line()
elif self.animation_type == "double_arc":
self._draw_canvas_double_arc()
elif self.animation_type == "counter_arc":
self._draw_canvas_counter_arc()
elif self.animation_type == "blink":
self._draw_canvas_blink()
def _draw_canvas_pulse(self):
"""Draws the pulse animation using canvas methods."""
center_x, center_y = self.width / 2, self.height / 2
alpha = (sin(self.angle * 5) + 1) / 2 # Faster pulse
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
pulse_color = f"#{r:02x}{g:02x}{b:02x}"
if self.animation_type == "line":
for i in range(8):
angle = i * (pi / 4)
start_x = center_x + cos(angle) * (self.width * 0.2)
start_y = center_y + sin(angle) * (self.height * 0.2)
end_x = center_x + cos(angle) * (self.width * 0.4)
end_y = center_y + sin(angle) * (self.height * 0.4)
self.create_line(start_x, start_y, end_x,
end_y, fill=pulse_color, width=2)
elif self.animation_type == "double_arc":
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
self.create_arc(bbox, start=0, extent=359.9,
style=tk.ARC, outline=pulse_color, width=2)
elif self.animation_type == "counter_arc":
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
self.create_arc(bbox_outer, start=0, extent=359.9,
style=tk.ARC, outline=pulse_color, width=2)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
self.create_arc(bbox_inner, start=0, extent=359.9,
style=tk.ARC, outline=self.color, width=2)
def _draw_canvas_line(self):
"""Draws the line animation using canvas methods."""
center_x, center_y = self.width / 2, self.height / 2
for i in range(8):
angle = self.angle + i * (pi / 4)
start_x = center_x + cos(angle) * (self.width * 0.2)
start_y = center_y + sin(angle) * (self.height * 0.2)
end_x = center_x + cos(angle) * (self.width * 0.4)
end_y = center_y + sin(angle) * (self.height * 0.4)
alpha = (cos(self.angle * 2 + i * (pi / 4)) + 1) / 2
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
color = f"#{r:02x}{g:02x}{b:02x}"
self.create_line(start_x, start_y, end_x,
end_y, fill=color, width=2)
def _draw_canvas_double_arc(self):
"""Draws the double arc animation using canvas methods."""
center_x, center_y = self.width / 2, self.height / 2
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
start_angle1 = -self.angle * 180 / pi
extent1 = 120 + 60 * sin(-self.angle)
self.create_arc(bbox, start=start_angle1, extent=extent1,
style=tk.ARC, outline=self.highlight_color, width=2)
start_angle2 = (-self.angle + pi) * 180 / pi
extent2 = 120 + 60 * sin(-self.angle + pi / 2)
self.create_arc(bbox, start=start_angle2, extent=extent2,
style=tk.ARC, outline=self.color, width=2)
def _draw_canvas_counter_arc(self):
"""Draws the counter arc animation using canvas methods."""
center_x, center_y = self.width / 2, self.height / 2
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
start_angle1 = -self.angle * 180 / pi
self.create_arc(bbox_outer, start=start_angle1, extent=150,
style=tk.ARC, outline=self.highlight_color, width=2)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
start_angle2 = self.angle * 180 / pi + 60
self.create_arc(bbox_inner, start=start_angle2, extent=150,
style=tk.ARC, outline=self.color, width=2)
def _draw_canvas_blink(self):
"""Draws the blink animation using canvas methods."""
center_x, center_y = self.width / 2, self.height / 2
radius = min(center_x, center_y) * 0.8
alpha = (sin(self.angle * 2) + 1) / 2 # Slower blinking speed
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
blink_color = f"#{r:02x}{g:02x}{b:02x}"
self.create_arc(center_x - radius, center_y - radius, center_x + radius, center_y +
radius, start=0, extent=359.9, style=tk.ARC, outline=blink_color, width=4)
def _draw_pillow_frame(self):
"""Draws a frame using Pillow for anti-aliased graphics."""
self.draw.rectangle(
[0, 0, self.width * 4, self.height * 4], fill=(0, 0, 0, 0))
if self.pulse_animation:
self._draw_pillow_pulse()
elif self.animation_type == "line":
self._draw_pillow_line()
elif self.animation_type == "double_arc":
self._draw_pillow_double_arc()
elif self.animation_type == "counter_arc":
self._draw_pillow_counter_arc()
elif self.animation_type == "blink":
self._draw_pillow_blink()
resized_image = self.image.resize(
(self.width, self.height), Image.Resampling.LANCZOS)
self.photo_image = ImageTk.PhotoImage(resized_image)
self.delete("all")
self.create_image(0, 0, anchor="nw", image=self.photo_image)
def _draw_pillow_pulse(self):
"""Draws the pulse animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
alpha = (sin(self.angle * 5) + 1) / 2 # Faster pulse
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
pulse_color = (r, g, b)
if self.animation_type == "line":
for i in range(12):
angle = i * (pi / 6)
start_x = center_x + cos(angle) * (self.width * 0.8)
start_y = center_y + sin(angle) * (self.height * 0.8)
end_x = center_x + cos(angle) * (self.width * 1.6)
end_y = center_y + sin(angle) * (self.height * 1.6)
self.draw.line([(start_x, start_y), (end_x, end_y)],
fill=pulse_color, width=6, joint="curve")
elif self.animation_type == "double_arc":
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
self.draw.arc(bbox, start=0, end=360, fill=pulse_color, width=5)
elif self.animation_type == "counter_arc":
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
self.draw.arc(bbox_outer, start=0, end=360,
fill=pulse_color, width=7)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
self.draw.arc(bbox_inner, start=0, end=360,
fill=self.color_rgb, width=7)
def _draw_pillow_line(self):
"""Draws the line animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
for i in range(12):
angle = self.angle + i * (pi / 6)
start_x = center_x + cos(angle) * (self.width * 0.8)
start_y = center_y + sin(angle) * (self.height * 0.8)
end_x = center_x + cos(angle) * (self.width * 1.6)
end_y = center_y + sin(angle) * (self.height * 1.6)
alpha = (cos(self.angle * 2.5 + i * (pi / 6)) + 1) / 2
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
color = (r, g, b)
self.draw.line([(start_x, start_y), (end_x, end_y)],
fill=color, width=6, joint="curve")
def _draw_pillow_double_arc(self):
"""Draws the double arc animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
start_angle1 = self.angle * 180 / pi
extent1 = 120 + 60 * sin(self.angle)
self.draw.arc(bbox, start=start_angle1, end=start_angle1 +
extent1, fill=self.highlight_color_rgb, width=5)
start_angle2 = (self.angle + pi) * 180 / pi
extent2 = 120 + 60 * sin(self.angle + pi / 2)
self.draw.arc(bbox, start=start_angle2, end=start_angle2 +
extent2, fill=self.color_rgb, width=5)
def _draw_pillow_counter_arc(self):
"""Draws the counter arc animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
start_angle1 = self.angle * 180 / pi
self.draw.arc(bbox_outer, start=start_angle1, end=start_angle1 +
150, fill=self.highlight_color_rgb, width=7)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
start_angle2 = -self.angle * 180 / pi + 60
self.draw.arc(bbox_inner, start=start_angle2,
end=start_angle2 + 150, fill=self.color_rgb, width=7)
def _draw_pillow_blink(self):
"""Draws the blink animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius = min(center_x, center_y) * 0.8
alpha = (sin(self.angle * 2) + 1) / 2 # Slower blinking speed
r = int(
alpha * (self.highlight_color_rgb[0] - self.color_rgb[0]) + self.color_rgb[0])
g = int(
alpha * (self.highlight_color_rgb[1] - self.color_rgb[1]) + self.color_rgb[1])
b = int(
alpha * (self.highlight_color_rgb[2] - self.color_rgb[2]) + self.color_rgb[2])
blink_color = (r, g, b)
self.draw.arc((center_x - radius, center_y - radius, center_x + radius,
center_y + radius), start=0, end=360, fill=blink_color, width=10)
def _draw_stopped_frame(self):
"""Draws the icon in its stopped (static) state."""
self.delete("all")
if self.use_pillow:
self._draw_pillow_stopped_frame()
else:
self._draw_canvas_stopped_frame()
def _draw_canvas_stopped_frame(self):
"""Draws the stopped state using canvas methods."""
if self.animation_type == "line":
self._draw_canvas_line_stopped()
elif self.animation_type == "double_arc":
self._draw_canvas_double_arc_stopped()
elif self.animation_type == "counter_arc":
self._draw_canvas_counter_arc_stopped()
elif self.animation_type == "blink":
self._draw_canvas_blink_stopped()
def _draw_canvas_line_stopped(self):
"""Draws the stopped state for the line animation."""
center_x, center_y = self.width / 2, self.height / 2
for i in range(8):
angle = i * (pi / 4)
start_x = center_x + cos(angle) * (self.width * 0.2)
start_y = center_y + sin(angle) * (self.height * 0.2)
end_x = center_x + cos(angle) * (self.width * 0.4)
end_y = center_y + sin(angle) * (self.height * 0.4)
self.create_line(start_x, start_y, end_x, end_y,
fill=self.highlight_color, width=2)
def _draw_canvas_double_arc_stopped(self):
"""Draws the stopped state for the double arc animation."""
center_x, center_y = self.width / 2, self.height / 2
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
self.create_arc(bbox, start=0, extent=359.9, style=tk.ARC,
outline=self.highlight_color, width=2)
def _draw_canvas_counter_arc_stopped(self):
"""Draws the stopped state for the counter arc animation."""
center_x, center_y = self.width / 2, self.height / 2
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
self.create_arc(bbox_outer, start=0, extent=359.9,
style=tk.ARC, outline=self.highlight_color, width=2)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
self.create_arc(bbox_inner, start=0, extent=359.9,
style=tk.ARC, outline=self.color, width=2)
def _draw_canvas_blink_stopped(self):
"""Draws the stopped state for the blink animation."""
center_x, center_y = self.width / 2, self.height / 2
radius = min(center_x, center_y) * 0.8
self.create_arc(center_x - radius, center_y - radius, center_x + radius, center_y +
radius, start=0, extent=359.9, style=tk.ARC, outline=self.highlight_color, width=4)
def _draw_pillow_stopped_frame(self):
"""Draws the stopped state using Pillow."""
self.draw.rectangle(
[0, 0, self.width * 4, self.height * 4], fill=(0, 0, 0, 0))
if self.animation_type == "line":
self._draw_pillow_line_stopped()
elif self.animation_type == "double_arc":
self._draw_pillow_double_arc_stopped()
elif self.animation_type == "counter_arc":
self._draw_pillow_counter_arc_stopped()
elif self.animation_type == "blink":
self._draw_pillow_blink_stopped()
resized_image = self.image.resize(
(self.width, self.height), Image.Resampling.LANCZOS)
self.photo_image = ImageTk.PhotoImage(resized_image)
self.create_image(0, 0, anchor="nw", image=self.photo_image)
def _draw_pillow_line_stopped(self):
"""Draws the stopped state for the line animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
for i in range(12):
angle = i * (pi / 6)
start_x = center_x + cos(angle) * (self.width * 0.8)
start_y = center_y + sin(angle) * (self.height * 0.8)
end_x = center_x + cos(angle) * (self.width * 1.6)
end_y = center_y + sin(angle) * (self.height * 1.6)
self.draw.line([(start_x, start_y), (end_x, end_y)],
fill=self.highlight_color_rgb, width=6, joint="curve")
def _draw_pillow_double_arc_stopped(self):
"""Draws the stopped state for the double arc animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius = min(center_x, center_y) * 0.8
bbox = (center_x - radius, center_y - radius,
center_x + radius, center_y + radius)
self.draw.arc(bbox, start=0, end=360,
fill=self.highlight_color_rgb, width=5)
def _draw_pillow_counter_arc_stopped(self):
"""Draws the stopped state for the counter arc animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius_outer = min(center_x, center_y) * 0.8
bbox_outer = (center_x - radius_outer, center_y - radius_outer,
center_x + radius_outer, center_y + radius_outer)
self.draw.arc(bbox_outer, start=0, end=360,
fill=self.highlight_color_rgb, width=7)
radius_inner = min(center_x, center_y) * 0.6
bbox_inner = (center_x - radius_inner, center_y - radius_inner,
center_x + radius_inner, center_y + radius_inner)
self.draw.arc(bbox_inner, start=0, end=360,
fill=self.color_rgb, width=7)
def _draw_pillow_blink_stopped(self):
"""Draws the stopped state for the blink animation using Pillow."""
center_x, center_y = self.width * 2, self.height * 2
radius = min(center_x, center_y) * 0.8
self.draw.arc((center_x - radius, center_y - radius, center_x + radius,
center_y + radius), start=0, end=360, fill=self.highlight_color_rgb, width=10)
def _animate(self):
"""The main animation loop."""
if self.running:
self.angle += 0.1
if self.angle > 2 * pi:
self.angle -= 2 * pi
self._draw_frame()
self.after(30, self._animate)
def start(self, pulse=False):
"""
Starts the animation.
Args:
pulse (bool): If True, plays a pulsing animation instead of the main one.
"""
if not self.running:
self.pulse_animation = pulse
self.running = True
self._animate()
def stop(self):
"""Stops the animation and shows the static 'stopped' frame."""
self.running = False
self.pulse_animation = False
self._draw_stopped_frame()
def hide(self):
"""Stops the animation and clears the canvas."""
self.running = False
self.pulse_animation = False
self.delete("all")
def show_full_circle(self):
"""Shows the static 'stopped' frame without starting the animation."""
if not self.running:
self._draw_stopped_frame()

308
cls_mth_fc.py Executable file
View File

@@ -0,0 +1,308 @@
""" Classes Method and functions for lx apps """
import gettext
import locale
import os
import shutil
import subprocess
from subprocess import check_call
import tkinter as tk
import zipfile
from datetime import datetime
from pathlib import Path
from tkinter import ttk
import requests
APP = 'wirepy'
LOCALE_DIR = "/usr/share/locale/"
locale.bindtextdomain(APP, LOCALE_DIR)
gettext.bindtextdomain(APP, LOCALE_DIR)
gettext.textdomain(APP)
_ = gettext.gettext
def dirs_and_files():
pth = Path.home() / '.config/wire_py'
pth.mkdir(parents=True, exist_ok=True)
sett = Path.home() / '.config/wire_py/settings'
ks = Path.home() / '.config/wire_py/keys'
if sett.exists():
pass
else:
sett.touch()
sett.write_text('[UPDATES]\non\n[THEME]\nlight\n[TOOLTIP]\nTrue\n[AUTOSTART ON]\noff')
if ks.exists():
pass
else:
ks.touch()
def files_for_autostart():
pth2 = Path.home() / '.config/systemd/user'
pth2.mkdir(parents=True, exist_ok=True)
wg_ser = Path.home() / '.config/systemd/user/wg_start.service'
if wg_ser.exists():
pass
else:
wg_ser.touch()
sett.write_text('[Unit]\nDescription=Automatic Tunnel Start\nAfter=network-online.target\n\n[Service]\nType=oneshot\nExecStartPre=/bin/sleep 5\nExecStart=/usr/local/bin/start_wg.py\n[Install]\nWantedBy=default.target')
check_call(['systemctl', '--user', 'enable', wg_start.service])
wg_set = Path(Path.home() / '.config/wire_py/settings')
class GiteaUpdate:
"""
Calling api_down requests the URL and the version of the running script.
Example: version = 'v. 1.1.1.1' GiteaUpdate.api_down(http://example.de, version)
Calling download requests the download URL of the running script,
the taskbar image for the “Download OK” window, the taskbar image for the
“Download error” window and the variable res
"""
@staticmethod
def api_down(update_api_url, version):
try:
response = requests.get(update_api_url)
response_dict = response.json()
response_dict = response_dict[0]
with open(wg_set, 'r') as set_file:
set_file = set_file.read()
if 'on\n' in set_file:
if version[3:] != response_dict['tag_name']:
return response_dict['tag_name']
else:
return 'No Updates'
else:
return 'False'
except requests.exceptions.ConnectionError:
return 'No Internet Connection!'
@staticmethod
def download(urld, down_ok_image, down_not_ok_image, res):
try:
to_down = 'wget -qP ' + str(Path.home()) + ' ' + urld
result = subprocess.call(to_down, shell=True)
if result == 0:
shutil.chown(str(Path.home()) + f'/{res}.zip', 1000, 1000)
"""img_w, img_i, w_title, w_txt hand over"""
iw = r'/usr/share/icons/lx-icons/64/info.png'
ii = down_ok_image
wt = _('Download Successful')
msg_t = _('Your zip file is in home directory')
msg_window(iw, ii, wt, msg_t)
else:
"""img_w, img_i, w_title, w_txt hand over"""
iw = r'/usr/share/icons/lx-icons/64/error.png'
ii = down_not_ok_image
wt = _('Download error')
msg_t = _('Download failed! Please try again')
msg_window(iw, ii, wt, msg_t)
except subprocess.CalledProcessError:
"""img_w, img_i, w_title, w_txt hand over"""
iw = r'/usr/share/icons/lx-icons/64/error.png'
ii = down_not_ok_image
wt = _('Download error')
msg_t = _('Download failed! No internet connection!')
msg_window(iw, ii, wt, msg_t)
def msg_window(img_w, img_i, w_title, w_txt, txt2=None, com=None):
"""
Function for different message windows for the user. with 4 arguments to be passed.
To create messages with your own images, icons, and titles. As an alternative to Python Messagebox.
Paths to images must be specified: r'/usr/share/icons/lx-icons/64/info.png'
img_w = Image for Tk Window
img_i = Image for Icon
w_title = Windows Title
w_txt = Text for Tk Window
txt2 = Text for Button two
com = function for Button command
"""
msg = tk.Toplevel()
msg.resizable(width=False, height=False)
msg.title(w_title)
msg.configure(pady=15, padx=15)
msg.img = tk.PhotoImage(file=img_w)
msg.i_window = tk.Label(msg, image=msg.img)
label = tk.Label(msg, text=w_txt)
label.grid(column=1, row=0)
if txt2 is not None and com is not None:
label.config(font=('Ubuntu', 11), padx=15, justify='left')
msg.i_window.grid(column=0, row=0, sticky='nw')
button2 = ttk.Button(msg, text=f'{txt2}', command=com, padding=4)
button2.grid(column=0, row=1, sticky='e', columnspan=2)
button = ttk.Button(msg, text='OK', command=msg.destroy, padding=4)
button.grid(column=0, row=1, sticky='w', columnspan=2)
else:
label.config(font=('Ubuntu', 11), padx=15)
msg.i_window.grid(column=0, row=0)
button = ttk.Button(msg, text='OK', command=msg.destroy, padding=4)
button.grid(column=0, columnspan=2, row=1)
img_i = tk.PhotoImage(file=img_i)
msg.iconphoto(True, img_i)
msg.columnconfigure(0, weight=1)
msg.rowconfigure(0, weight=1)
msg.winfo_toplevel()
class Tunnel:
"""
Class of Methods for Wire-Py
"""
"""
The config file is packed into a dictionary,
to display the values Address , DNS and Peer in the labels
"""
@classmethod
def con_to_dict(cls, file):
dictlist = []
for lines in file.readlines():
line_plit = lines.split()
dictlist = dictlist + line_plit
dictlist.remove('[Interface]')
dictlist.remove('[Peer]')
for items in dictlist:
if items == '=':
dictlist.remove(items)
if items == '::/0':
dictlist.remove(items)
''' Here is the beginning (Loop) of convert List to Dictionary '''
for _ in dictlist:
a = [dictlist[0], dictlist[1]]
b = [dictlist[2], dictlist[3]]
c = [dictlist[4], dictlist[5]]
d = [dictlist[6], dictlist[7]]
e = [dictlist[8], dictlist[9]]
f = [dictlist[10], dictlist[11]]
g = [dictlist[12], dictlist[13]]
h = [dictlist[14], dictlist[15]]
new_list = [a, b, c, d, e, f, g, h]
final_dict = {}
for elements in new_list:
final_dict[elements[0]] = elements[1]
''' end... result a Dictionary '''
address = final_dict['Address']
dns = final_dict['DNS']
if ',' in dns:
dns = dns[:-1]
endpoint = final_dict['Endpoint']
if 'PresharedKey' in final_dict:
pre_key = final_dict['PresharedKey']
else:
pre_key = final_dict['PreSharedKey']
return address, dns, endpoint, pre_key
"""
Shows the Active Tunnel
"""
@staticmethod
def active():
active = os.popen('nmcli con show --active | grep -iPo "(.*)(wireguard)"').read().split()
if not active:
active = ''
else:
active = active[0]
return active
"""
Shows all existing Wireguard tunnels a login user
"""
@staticmethod
def list():
dirname = Path.home() / '.config/wire_py/'
wg_s = os.listdir(dirname)
wg_s.remove('keys')
wg_s.remove('settings')
return wg_s
"""
This will export the tunnels.
A zipfile with current date and time is created
in the user's home directory with correct right
"""
@staticmethod
def export():
now_time = datetime.now()
now_datetime = now_time.strftime('wg-exp-' + '%m-%d-%Y' + '-' + '%H:%M')
tl = Tunnel.list()
try:
if len(tl) != 0:
wg_tar = str(Path.home()) + '/' + now_datetime
shutil.copytree(Path.home() / '.config/wire_py/', '/tmp/wire_py', dirs_exist_ok=True)
source = Path('/tmp/wire_py')
Path.unlink(Path(source) / 'wg_py', missing_ok=True)
Path.unlink(Path(source) / 'keys', missing_ok=True)
Path.unlink(Path(source) / 'settings', missing_ok=True)
shutil.make_archive(wg_tar, 'zip', source)
#shutil.chown(wg_tar + '.zip', 1000, 1000)
shutil.rmtree(source)
with zipfile.ZipFile((wg_tar + '.zip'), 'r') as zf:
if len(zf.namelist()) != 0:
"""img_w, img_i, w_title, w_txt hand over"""
iw = r'/usr/share/icons/lx-icons/64/info.png'
ii = r'/usr/share/icons/wp-icons/48/wg_vpn.png'
wt = _('Export Successful')
msg_t = _('Your zip file is in home directory')
msg_window(iw, ii, wt, msg_t)
else:
"""img_w, img_i, w_title, w_txt hand over"""
iw = r'/usr/share/icons/lx-icons/64/error.png'
ii = r'/usr/share/icons/wp-icons/48/wg_msg.png'
wt = _('Export error')
msg_t = _('Export failed! Please try again')
msg_window(iw, ii, wt, msg_t)
else:
"""img_w, img_i, w_title, w_txt hand over"""
iw = r'/usr/share/icons/lx-icons/64/info.png'
ii = r'/usr/share/icons/wp-icons/48/wg_msg.png'
wt = _('Select tunnel')
msg_t = _('Please first import tunnel')
msg_window(iw, ii, wt, msg_t)
except TypeError:
pass
class Tipi:
"""
Class for Tooltip setting write in File
Calling request path to file
"""
@staticmethod
def if_tip(path):
with open(path, 'r') as set_file2:
lines2 = set_file2.readlines()
if 'False\n' in lines2:
return False
else:
return True
wg_tips = Tipi.if_tip(wg_set)

133
install Executable file
View File

@@ -0,0 +1,133 @@
#!/bin/bash
NORMAL='\033[0m'
GREEN='\033[1;32m'
RED='\033[31;1;42m'
BLUE='\033[30;1;34m'
install_file_with(){
clear
mkdir -p ~/.config/wire_py && touch ~/.config/wire_py/keys && cp -u settings ~/.config/wire_py/ && \
mkdir -p ~/.config/systemd/user && cp -u wg_start.service ~/.config/systemd/user/ && \
systemctl --user enable wg_start.service
sudo apt install python3-tk && \
sudo cp -u wg_main.py start_wg.py cls_mth_fc.py /usr/local/bin/ && \
sudo cp -uR wp-icons lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
sudo ln -sf /usr/local/bin/wg_main.py /usr/local/bin/wirepy && \
sudo cp -u Wire-Py.desktop /usr/share/applications/
}
install_arch_d(){
clear
sudo pacman -S --noconfirm tk python3 python-requests && \
sudo cp -u wg_main.py start_wg.py cls_mth_fc.py && \
sudo mkdir -p /etc/wire_py && sudo touch /etc/wire_py/keys && sudo cp -u settings /etc/wire_py/ && \
sudo cp -uR wp-icons lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
sudo chown -R root:root /etc/wire_py && sudo chmod 755 /etc/wire_py && \
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
sudo ln -sf /usr/bin/wirepy.py /usr/local/bin/wirepy && \
sudo cp -u org.wirepy.policy /usr/share/polkit-1/actions/ && \
sudo cp -u Wire-Py.desktop /usr/share/applications/ && \
sudo cp -u wg_start.service /lib/systemd/system/ && \
sudo systemctl enable wg_start.service
}
if grep -i 'debian' /etc/os-release > /dev/null 2>&1
then
groups > /tmp/isgroup
if grep 'sudo' /tmp/isgroup
then
install_file_with
else
echo -e "$BLUE"The installer found that they are not in the group sudo.""
echo -e "with "$RED"su -"$BLUE" "they can enter the root shell in which they then""
echo -e "enter "$GREEN""usermod -aG sudo $USER.""$BLUE""
echo -e ""after logging in from the system, they can then run Wire-Py install again." $NORMAL"
read -n 1 -s -r -p $"To close the Window press a button"
clear
exit 0
fi
elif grep -i 'mint\|ubuntu\|pop|' /etc/os-release > /dev/null 2>&1
then
install_file_with
elif grep -i 'arch' /etc/os-release > /dev/null 2>&1
then
groups > /tmp/isgroup
clear
if grep 'wheel' /tmp/isgroup
then
install_arch_d
else
echo "The installer found that they are not in the group sudo."
echo "The sudoers file must be edited with"
echo -e "$RED""su -""$NORMAL"
echo -e "$GREEN"""EDITOR=nano visudo"""$NORMAL"
echo "Find the line:"
echo "## Uncomment to allow members of group wheel to execute any command"
echo "remove '#' on # %wheel ALL=(ALL) ALL and save the file"
echo -e "then enter "$GREEN"gpasswd -a $USER wheel.""$NORMAL"
echo "after logging in from the system, they can then run Wire-Py install again."
read -n 1 -s -r -p $"To close the Window press a button"
clear
exit 0
fi
elif grep -i '|manjaro\|garuda\|endeavour|' /etc/os-release > /dev/null 2>&1
then
install_arch_d
elif grep -i 'fedora' /etc/os-release > /dev/null 2>&1
then
if ! which python3-tkinter &> /dev/null
then sudo dnf install python3-tkinter -y
sudo cp -u wg_main.py start_wg.py cls_mth_fc.py && \
sudo mkdir -p /etc/wire_py && sudo touch /etc/wire_py/keys && \
sudo cp -u settings /etc/wire_py/ && \
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
sudo cp -uR wp-icons lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
sudo chown -R root:root /etc/wire_py && sudo chmod 755 /etc/wire_py && \
sudo ln -sf /usr/bin/wirepy.py /usr/local/bin/wirepy && \
sudo cp -u org.wirepy.policy /usr/share/polkit-1/actions/ && \
sudo cp -u Wire-Py.desktop /usr/share/applications/ && \
sudo cp -u wg_start.service /lib/systemd/system/ && \
sudo systemctl enable wg_start.service
fi
elif grep -i 'suse' /etc/os-release > /dev/null 2>&1
then
if ! which python311-tk &> /dev/null
then sudo zypper install python311-tk
sudo cp -u wg_main.py start_wg.py cls_mth_fc.py && \
sudo mkdir -p /etc/wire_py && sudo touch /etc/wire_py/keys && \
sudo cp -u settings /etc/wire_py/ && \
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
sudo cp -uR wp-icons lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
sudo chown -R root:root /etc/wire_py && sudo chmod 755 /etc/wire_py && \
sudo ln -sf /usr/bin/wirepy.py /usr/local/bin/wirepy && \
sudo cp -u org.wirepy.policy /usr/share/polkit-1/actions/ && \
sudo cp -u Wire-Py.desktop /usr/share/applications/ && \
sudo cp -u wg_start.service /lib/systemd/system/ && \
sudo systemctl enable wg_start.service
fi
else
clear
echo $"Your System could not be determined."
echo
read -n 1 -s -r -p $"To close the window press a button"
clear
exit 0
fi
clear
read -n 1 -s -r -p $"To close the Window press a button"
clear

Binary file not shown.

View File

@@ -1,13 +0,0 @@
class Logger:
def __init__(self):
self._log_func = print # Default to print if not initialized
def init_logger(self, log_func):
self._log_func = log_func
def log(self, message):
self._log_func(message)
# Global instance
app_logger = Logger()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 969 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1014 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 B

Some files were not shown because too many files have changed in this diff Show More