Photo-Composition-Designer 1.0.1__tar.gz → 1.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. photo_composition_designer-1.0.2/AGENTS.md +94 -0
  2. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/HISTORY.md +43 -0
  3. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/LICENSE +29 -0
  4. {photo_composition_designer-1.0.1/src/Photo_Composition_Designer.egg-info → photo_composition_designer-1.0.2}/PKG-INFO +3 -2
  5. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/config.yaml +14 -0
  6. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/usage/config.md +21 -14
  7. photo_composition_designer-1.0.2/images/descriptions.txt +9 -0
  8. photo_composition_designer-1.0.2/images/week_4/image_13.jpg +0 -0
  9. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/pyproject.toml +2 -1
  10. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/_version.py +3 -3
  11. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/common/Photo.py +3 -7
  12. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/config/config.py +13 -16
  13. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/core/base.py +21 -4
  14. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/gui/GuiLogWriter.py +9 -3
  15. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/gui/gui.py +104 -38
  16. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/image/CalendarRenderer.py +4 -1
  17. photo_composition_designer-1.0.2/src/Photo_Composition_Designer/image/CollageRenderer.py +734 -0
  18. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/image/DescriptionRenderer.py +12 -10
  19. photo_composition_designer-1.0.2/src/Photo_Composition_Designer/image/ObjectDetector.py +257 -0
  20. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/image/SmartCrop.py +57 -49
  21. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/tools/ImageDistributor.py +2 -1
  22. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2/src/Photo_Composition_Designer.egg-info}/PKG-INFO +3 -2
  23. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer.egg-info/SOURCES.txt +3 -1
  24. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer.egg-info/requires.txt +2 -1
  25. photo_composition_designer-1.0.2/src/Photo_Composition_Designer.egg-info/scm_file_list.json +157 -0
  26. photo_composition_designer-1.0.2/src/Photo_Composition_Designer.egg-info/scm_version.json +8 -0
  27. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/firewall_handler.py +4 -1
  28. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_CollageRenderer.py +44 -44
  29. photo_composition_designer-1.0.2/tests/test_ObjectDetector.py +100 -0
  30. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_SmartCrop.py +18 -2
  31. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_cli.py +2 -2
  32. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/uv.lock +19 -4
  33. photo_composition_designer-1.0.1/.idea/workspace.xml +0 -836
  34. photo_composition_designer-1.0.1/images/descriptions.txt +0 -6
  35. photo_composition_designer-1.0.1/images/week_5/image_13.jpg +0 -0
  36. photo_composition_designer-1.0.1/src/Photo_Composition_Designer/image/CollageRenderer.py +0 -583
  37. photo_composition_designer-1.0.1/src/Photo_Composition_Designer/image/ObjectDetector.py +0 -112
  38. photo_composition_designer-1.0.1/tests/test_ObjectDetector.py +0 -48
  39. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/FUNDING.yml +0 -0
  40. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  41. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  42. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  43. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/actions/setup-environment/action.yml +0 -0
  44. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/dependabot.yml +0 -0
  45. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/init.sh +0 -0
  46. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/release_message.sh +0 -0
  47. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/rename_project.sh +0 -0
  48. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/update_funding.py +0 -0
  49. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/workflows/build-linux.yml +0 -0
  50. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/workflows/build-macos.yml +0 -0
  51. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/workflows/build-win.yml +0 -0
  52. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/workflows/main.yml +0 -0
  53. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/workflows/release.yml +0 -0
  54. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.github/workflows/update_readme.yml +0 -0
  55. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.gitignore +0 -0
  56. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.idea/runConfigurations/config_generate.xml +0 -0
  57. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.idea/runConfigurations/module_cli.xml +0 -0
  58. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.idea/runConfigurations/module_gui.xml +0 -0
  59. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.idea/vcs.xml +0 -0
  60. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.pre-commit-config-linux.yaml +0 -0
  61. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.pre-commit-config.yaml +0 -0
  62. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/.readthedocs.yaml +0 -0
  63. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/Makefile +0 -0
  64. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/README.md +0 -0
  65. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/anniversaries.ini +0 -0
  66. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/.nav.yml +0 -0
  67. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/_static/img/collages.png +0 -0
  68. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/_static/img/favicon.png +0 -0
  69. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/_static/img/gui_main.png +0 -0
  70. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/_static/img/gui_settings.png +0 -0
  71. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/_static/img/logo.png +0 -0
  72. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/css/custom.css +0 -0
  73. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/funding/funding.md +0 -0
  74. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/getting-started/install.md +0 -0
  75. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/getting-started/virtual-environment.md +0 -0
  76. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/index.md +0 -0
  77. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/docs/usage/cli.md +0 -0
  78. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/title/This is a description for all photos of this calender collage page. It will be displayed in one line between the photos and the calendar items..txt +0 -0
  79. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/title/image_02.jpg +0 -0
  80. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_1/description.txt +0 -0
  81. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_1/image_01.jpg +0 -0
  82. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_2/image_04.jpg +0 -0
  83. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_2/image_13.jpg +0 -0
  84. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_3/This is a description for all photos of this calender collage page. It will be displayed in one line between the photos and the calendar items..txt +0 -0
  85. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_3/image_02.jpg +0 -0
  86. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_3/image_03.jpg +0 -0
  87. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_3/image_07.jpg +0 -0
  88. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_4/image_08.jpg +0 -0
  89. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_4/image_09.jpg +0 -0
  90. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_4/image_10.jpg +0 -0
  91. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_5/Lisbon.jpg +0 -0
  92. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_5/image_05.jpg +0 -0
  93. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_5/image_06.jpg +0 -0
  94. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_5/image_07.jpg +0 -0
  95. {photo_composition_designer-1.0.1/images/week_4 → photo_composition_designer-1.0.2/images/week_5}/image_13.jpg +0 -0
  96. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_6_testimages_3/bundschatten-sheep-7039994_1920.jpg +0 -0
  97. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_6_testimages_3/falkenpost-cars-1638594_1920.jpg +0 -0
  98. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_6_testimages_3/valdemarvan2003-vietnam-8121062_1920.jpg +0 -0
  99. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_7_testimages_4/fietzfotos-london-2928889_1920.jpg +0 -0
  100. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_7_testimages_4/piper60-spanish-84795_1920.jpg +0 -0
  101. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_7_testimages_4/sippakorn-cows-4708901_1920.jpg +0 -0
  102. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_7_testimages_4/tuananhngo-street-vendor-4176310_1920.jpg +0 -0
  103. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_8_testimages_5/landscape_one_bicycle_left.jpg +0 -0
  104. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_8_testimages_5/landscape_two_persons_right.jpg +0 -0
  105. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_8_testimages_5/portrait_one_person_top.jpg +0 -0
  106. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_8_testimages_5/portrait_two_cars_bottom.jpg +0 -0
  107. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/images/week_8_testimages_5/portrait_two_persons_bottom.jpg +0 -0
  108. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/locations_de.ini +0 -0
  109. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/locations_en.ini +0 -0
  110. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/mkdocs.yml +0 -0
  111. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/pytest.ini +0 -0
  112. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_admin_0_countries/ne_50m_admin_0_countries.README.html +0 -0
  113. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_admin_0_countries/ne_50m_admin_0_countries.VERSION.txt +0 -0
  114. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_admin_0_countries/ne_50m_admin_0_countries.cpg +0 -0
  115. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_admin_0_countries/ne_50m_admin_0_countries.dbf +0 -0
  116. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_admin_0_countries/ne_50m_admin_0_countries.prj +0 -0
  117. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_admin_0_countries/ne_50m_admin_0_countries.shp +0 -0
  118. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_admin_0_countries/ne_50m_admin_0_countries.shx +0 -0
  119. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_rivers_lake_centerlines_scale_rank/ne_50m_rivers_lake_centerlines_scale_rank.README.html +0 -0
  120. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_rivers_lake_centerlines_scale_rank/ne_50m_rivers_lake_centerlines_scale_rank.VERSION.txt +0 -0
  121. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_rivers_lake_centerlines_scale_rank/ne_50m_rivers_lake_centerlines_scale_rank.cpg +0 -0
  122. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_rivers_lake_centerlines_scale_rank/ne_50m_rivers_lake_centerlines_scale_rank.dbf +0 -0
  123. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_rivers_lake_centerlines_scale_rank/ne_50m_rivers_lake_centerlines_scale_rank.prj +0 -0
  124. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_rivers_lake_centerlines_scale_rank/ne_50m_rivers_lake_centerlines_scale_rank.shp +0 -0
  125. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/maps/ne_50m_rivers_lake_centerlines_scale_rank/ne_50m_rivers_lake_centerlines_scale_rank.shx +0 -0
  126. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/res/yolo/yolo26n.onnx +0 -0
  127. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/scripts/generate_config_docs.py +0 -0
  128. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/scripts/project_structure.py +0 -0
  129. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/scripts/show_filelist.py +0 -0
  130. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/scripts/update_readme.py +0 -0
  131. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/setup.cfg +0 -0
  132. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/__init__.py +0 -0
  133. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/__main__.py +0 -0
  134. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/cli/__init__.py +0 -0
  135. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/cli/__main__.py +0 -0
  136. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/cli/cli.py +0 -0
  137. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/common/Anniversaries.py +0 -0
  138. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/common/Locations.py +0 -0
  139. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/common/MoonPhase.py +0 -0
  140. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/common/__init__.py +0 -0
  141. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/config/__init__.py +0 -0
  142. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/core/__init__.py +0 -0
  143. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/gui/__init__.py +0 -0
  144. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/gui/__main__.py +0 -0
  145. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/image/MapRenderer.py +0 -0
  146. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/image/__init__.py +0 -0
  147. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/tools/DescriptionsFileGenerator.py +0 -0
  148. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/tools/GeoPlotter.py +0 -0
  149. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/tools/Helpers.py +0 -0
  150. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer/tools/__init__.py +0 -0
  151. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer.egg-info/dependency_links.txt +0 -0
  152. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer.egg-info/entry_points.txt +0 -0
  153. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/Photo_Composition_Designer.egg-info/top_level.txt +0 -0
  154. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/__init__.py +0 -0
  155. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/main.py +0 -0
  156. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/src/path_handler.py +0 -0
  157. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/template.yml.url +0 -0
  158. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/TestHelper.py +0 -0
  159. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/__init__.py +0 -0
  160. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_CalendarRenderer.py +0 -0
  161. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_CompositionDesigner.py +0 -0
  162. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_DescriptionRenderer.py +0 -0
  163. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_DescriptionsFileGenerator.py +0 -0
  164. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_GeoPlotter.py +0 -0
  165. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_ImageDistributor.py +0 -0
  166. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_Locations.py +0 -0
  167. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_MapRenderer.py +0 -0
  168. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_MoonPhase.py +0 -0
  169. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_Photo.py +0 -0
  170. {photo_composition_designer-1.0.1 → photo_composition_designer-1.0.2}/tests/test_anniversaries.py +0 -0
@@ -0,0 +1,94 @@
1
+ # Agent Guidelines for Python Code Quality
2
+
3
+ This document provides guidelines for maintaining high-quality Python code. These rules MUST be followed by all AI coding agents and contributors.
4
+
5
+ ## Your Core Principles
6
+
7
+ All code you write MUST be fully optimized.
8
+
9
+ "Fully optimized" includes:
10
+
11
+ - maximizing algorithmic big-O efficiency for memory and runtime
12
+ - using parallelization and vectorization where appropriate
13
+ - following proper style conventions for the code language (e.g. maximizing code reuse (DRY))
14
+
15
+ ## Preferred Tools
16
+
17
+ - Use `uv` for Python package management and to create a `.venv` if it is not present.
18
+
19
+ ## Code Style and Formatting
20
+
21
+ - **MUST** use meaningful, descriptive variable and function names
22
+ - **MUST** follow PEP 8 style guidelines
23
+ - **MUST** use 4 spaces for indentation (never tabs)
24
+ - **NEVER** use emoji, or unicode that emulates emoji (e.g. ✓, ✗). The only exception is when writing tests and testing the impact of multibyte characters.
25
+ - Use snake_case for functions/variables, PascalCase for classes, UPPER_CASE for constants
26
+ - Limit line length to 100 characters (ruff formatter standard)
27
+ - **MUST** avoid including redundant comments which are tautological or self-demonstating (e.g. cases where it is easily parsable what the code does at a glance so the comment does)
28
+ - **MUST** avoid including comments which leak what this file contains, or leak the original user prompt, ESPECIALLY if it's irrelevant to the output code.
29
+
30
+ ## Documentation
31
+
32
+ - **MUST** include docstrings for all public functions, classes, and methods
33
+ - **MUST** document function parameters, return values, and exceptions raised
34
+ - Keep comments up-to-date with code changes
35
+
36
+ ## Type Hints
37
+
38
+ - **MUST** use type hints for all function signatures (parameters and return values)
39
+ - **NEVER** use `Any` type unless absolutely necessary
40
+ - **MUST** run mypy and resolve all type errors
41
+ - Use `Optional[T]` or `T | None` for nullable types
42
+
43
+ ## Error Handling
44
+
45
+ - **NEVER** silently swallow exceptions without logging
46
+ - **MUST** never use bare `except:` clauses
47
+ - **MUST** catch specific exceptions rather than broad exception types
48
+ - **MUST** use context managers (`with` statements) for resource cleanup
49
+ - Provide meaningful error messages
50
+
51
+ ## Function Design
52
+
53
+ - **MUST** keep functions focused on a single responsibility
54
+ - **NEVER** use mutable objects (lists, dicts) as default argument values
55
+ - Limit function parameters to 5 or fewer
56
+ - Return early to reduce nesting
57
+
58
+ ## Class Design
59
+
60
+ - **MUST** keep classes focused on a single responsibility
61
+ - **MUST** keep `__init__` simple; avoid complex logic
62
+ - Use dataclasses for simple data containers
63
+ - Prefer composition over inheritance
64
+
65
+ ## Testing
66
+
67
+ - **MUST** write unit tests for all new functions and classes
68
+ - **MUST** mock external dependencies (APIs, databases, file systems)
69
+ - **MUST** use pytest as the testing framework
70
+
71
+ ## Imports and Dependencies
72
+
73
+ - **MUST** avoid wildcard imports (`from module import *`)
74
+ - **MUST** document dependencies in `pyproject.toml`
75
+ - Use `uv` for fast package management and dependency resolution
76
+
77
+ ## Python Best Practices
78
+
79
+ - **NEVER** use mutable default arguments
80
+ - **MUST** use context managers (`with` statement) for file/resource management
81
+ - **MUST** use `is` for comparing with `None`, `True`, `False`
82
+ - **MUST** use f-strings for string formatting
83
+ - Use list comprehensions and generator expressions
84
+ - Use `enumerate()` instead of manual counter variables
85
+
86
+ ## Before Committing
87
+
88
+ - [ ] Code formatter pass: >> make format
89
+ - [ ] Linter pass: >> make lint
90
+ - [ ] All tests pass: >> make test
91
+
92
+ ---
93
+
94
+ **Remember:** Prioritize clarity and maintainability over cleverness. This is your core directive.
@@ -4,6 +4,49 @@ Changelog
4
4
 
5
5
  (unreleased)
6
6
  ------------
7
+ - Improve layouting: distribute 2x2 images more equally. Use image
8
+ importance weight where useful. Consider object area when calculating
9
+ importance. Clear cache at session start. [Paul Magister]
10
+ - No more workspace.xml in vcs. [Paul Magister]
11
+ - Calculate image weights better. [Paul Magister]
12
+ - Calculate image weights better. [Paul Magister]
13
+ - Improve reactivity: add file cache to store image recognition data.
14
+ [Paul Magister]
15
+ - Improve reactivity: render preview only as large as needed. [Paul
16
+ Magister]
17
+ - Update config-cli-gui: improved gui handling, reload last config.yaml,
18
+ move AppConfig into general base class. [Paul Magister]
19
+ - Use ttkbootstrap for modern gui design. [Paul Magister]
20
+ - CollageRenderer.py: imageScoreFactor: Factor how much the objects
21
+ (humans, animals, ...) in the image are considered for weighting the
22
+ image size. [Paul Magister]
23
+ - CollageRenderer.py: add rounded corner functionality: add parameter.
24
+ [Paul Magister]
25
+ - CollageRenderer.py: add rounded corner functionality. [Paul Magister]
26
+ - CollageRenderer.py: improve tests, more variants, shorter P and L for
27
+ portrait and landscape. Overweight portraits in one row so that they
28
+ get more space. [Paul Magister]
29
+ - Adjust weight in SmartCrop. [Paul Magister]
30
+ - Code formatting and import optimization. [Paul Magister]
31
+ - Algorithm finally working. [Paul Magister]
32
+ - Draft: spezielle Behandlung für 3: fast perfekt: fix gap bug. [Paul
33
+ Magister]
34
+ - Draft: spezielle Behandlung für 3: fast perfekt. [Paul Magister]
35
+ - Draft: spezielle Behandlung für 3. [Paul Magister]
36
+ - Draft: neues arrangement mit fitting algo: schon viel besser, aber bei
37
+ 3 Bildern geht es nicht. [Paul Magister]
38
+ - Draft: neues arrangement mit fitting algo: schon viel besser, aber bei
39
+ 3 Bildern geht es nicht. [Paul Magister]
40
+ - Draft: neues arrangement mit fitting algo. [Paul Magister]
41
+ - Make pytree: improvements on show_tree.py: doc external tool. [Paul
42
+ Magister]
43
+ - Improve cropping boxes and test visualization. [Paul Magister]
44
+ - CollageRenderer.py: rename private methods. [Paul Magister]
45
+
46
+
47
+ 1.0.1 (2026-06-14)
48
+ ------------------
49
+ - Docs: Update HISTORY.md for release 1.0.1. [Paul Magister]
7
50
  - Object recognition: improve cropping: prevent crash on None. [Paul
8
51
  Magister]
9
52
  - Object recognition: improve cropping. [Paul Magister]
@@ -22,3 +22,32 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
22
  OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
24
  For more information, please refer to <https://unlicense.org>
25
+
26
+ ----------
27
+
28
+ CollageRenderer.py:
29
+ Parts of the Collage Algorithm is adapted from
30
+ PyPhotoCollage/PhotoCollage.py
31
+ Thanks to Tim Wilson
32
+
33
+ MIT License
34
+
35
+ Copyright (c) 2020 Tim Wilson
36
+
37
+ Permission is hereby granted, free of charge, to any person obtaining a copy
38
+ of this software and associated documentation files (the "Software"), to deal
39
+ in the Software without restriction, including without limitation the rights
40
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41
+ copies of the Software, and to permit persons to whom the Software is
42
+ furnished to do so, subject to the following conditions:
43
+
44
+ The above copyright notice and this permission notice shall be included in all
45
+ copies or substantial portions of the Software.
46
+
47
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
53
+ SOFTWARE.
@@ -1,13 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Photo-Composition-Designer
3
- Version: 1.0.1
3
+ Version: 1.0.2
4
4
  Summary: Feature-rich Python project template for Photo-Composition-Designer.
5
5
  Author: pamagister
6
6
  Requires-Python: <4.0,>=3.11
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
9
  Requires-Dist: astral>=3.2
10
- Requires-Dist: config-cli-gui>=0.3.2
10
+ Requires-Dist: config-cli-gui>=0.3.5
11
+ Requires-Dist: ttkbootstrap>=1.20.3
11
12
  Requires-Dist: exifread>=3.5.1
12
13
  Requires-Dist: geopandas>=1.1.1
13
14
  Requires-Dist: holidays>=0.84
@@ -1,6 +1,16 @@
1
1
  app:
2
+ # Date format to use | type=str
3
+ date_format: '%Y-%m-%d'
2
4
  # Logging level for the application | type=str | choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
3
5
  log_level: INFO
6
+ # Maximum log file size in MB before rotation | type=int
7
+ log_file_max_size: 2
8
+ # Enable logging to file | type=bool | choices=[True, False]
9
+ enable_file_logging: true
10
+ # Enable logging to console | type=bool | choices=[True, False]
11
+ enable_console_logging: true
12
+ # GUI theme setting supported by ttkbootstrap | type=str | choices=['cosmo', 'flatly', 'litera', 'minty', 'lumen', 'sandstone', 'yeti', 'pulse', 'united', 'darkly', 'superhero', 'solar', 'cyborg', 'vapor', 'simplex']
13
+ theme: darkly
4
14
  general:
5
15
  # Path to the directory containing photos (absolute, or relative to this config.ini file) | type=PosixPath | [CLI]
6
16
  photoDirectory: images
@@ -61,6 +71,10 @@ layout:
61
71
  marginSides: 3
62
72
  # Spacing between elements in mm | type=int
63
73
  spacing: 2
74
+ # Use rounded corners at the edges of the images in the collage | type=bool | choices=[True, False]
75
+ useRoundedCorners: true
76
+ # Factor how much the objects (humans, animals, ...) in the image are considered for weighting the image size | type=float
77
+ imageScoreFactor: 2.0
64
78
  # Use short weekday names (e.g., Mon, Tue) | type=bool | choices=[True, False]
65
79
  useShortDayNames: false
66
80
  # Use short month names (e.g., Jan, Feb) | type=bool | choices=[True, False]
@@ -5,9 +5,14 @@ The parameters in the cli category can be accessed via the command line interfac
5
5
 
6
6
  ## Category "app"
7
7
 
8
- | Name | Type | Description | Default | Choices |
9
- |-----------|------|-----------------------------------|---------|---------------------------------------------------|
10
- | log_level | str | Logging level for the application | 'INFO' | ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] |
8
+ | Name | Type | Description | Default | Choices |
9
+ |------------------------|------|---------------------------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
10
+ | date_format | str | Date format to use | '%Y-%m-%d' | - |
11
+ | log_level | str | Logging level for the application | 'INFO' | ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] |
12
+ | log_file_max_size | int | Maximum log file size in MB before rotation | 2 | - |
13
+ | enable_file_logging | bool | Enable logging to file | True | [True, False] |
14
+ | enable_console_logging | bool | Enable logging to console | True | [True, False] |
15
+ | theme | str | GUI theme setting supported by ttkbootstrap | 'darkly' | ['cosmo', 'flatly', 'litera', 'minty', 'lumen', 'sandstone', 'yeti', 'pulse', 'united', 'darkly', 'superhero', 'solar', 'cyborg', 'vapor', 'simplex'] |
11
16
 
12
17
  ## Category "general"
13
18
 
@@ -59,15 +64,17 @@ The parameters in the cli category can be accessed via the command line interfac
59
64
 
60
65
  ## Category "layout"
61
66
 
62
- | Name | Type | Description | Default | Choices |
63
- |---------------------|------|-----------------------------------------------------------------------------------------------------|---------|---------------|
64
- | marginTop | int | Top margin in mm | 6 | - |
65
- | marginBottom | int | Bottom margin in mm | 3 | - |
66
- | marginSides | int | Side margins in mm | 3 | - |
67
- | spacing | int | Spacing between elements in mm | 2 | - |
68
- | useShortDayNames | bool | Use short weekday names (e.g., Mon, Tue) | False | [True, False] |
69
- | useShortMonthNames | bool | Use short month names (e.g., Jan, Feb) | True | [True, False] |
70
- | usePhotoDescription | bool | Include photo descriptions in the collage | True | [True, False] |
71
- | generatePdf | bool | Combine all generated collages into one pdf | True | [True, False] |
72
- | objectRecognition | bool | Use neuronal network YOLO (You Only Look Once) object detection model to crop images content-aware. | True | [True, False] |
67
+ | Name | Type | Description | Default | Choices |
68
+ |---------------------|-------|-------------------------------------------------------------------------------------------------------------|---------|---------------|
69
+ | marginTop | int | Top margin in mm | 6 | - |
70
+ | marginBottom | int | Bottom margin in mm | 3 | - |
71
+ | marginSides | int | Side margins in mm | 3 | - |
72
+ | spacing | int | Spacing between elements in mm | 2 | - |
73
+ | useRoundedCorners | bool | Use rounded corners at the edges of the images in the collage | True | [True, False] |
74
+ | imageScoreFactor | float | Factor how much the objects (humans, animals, ...) in the image are considered for weighting the image size | 2.0 | - |
75
+ | useShortDayNames | bool | Use short weekday names (e.g., Mon, Tue) | False | [True, False] |
76
+ | useShortMonthNames | bool | Use short month names (e.g., Jan, Feb) | True | [True, False] |
77
+ | usePhotoDescription | bool | Include photo descriptions in the collage | True | [True, False] |
78
+ | generatePdf | bool | Combine all generated collages into one pdf | True | [True, False] |
79
+ | objectRecognition | bool | Use neuronal network YOLO (You Only Look Once) object detection model to crop images content-aware. | True | [True, False] |
73
80
 
@@ -0,0 +1,9 @@
1
+ title: Description text for week title
2
+ week_1: Description text for week week_1
3
+ week_2: Description text for week week_2
4
+ week_3: Description text for week week_3
5
+ week_4: Description text for week week_4
6
+ week_5: Description text for week week_5
7
+ week_6_testimages_3: Description text for week week_6_testimages_3
8
+ week_7_testimages_4: Description text for week week_7_testimages_4
9
+ week_8_testimages_5: Description text for week week_8_testimages_5
@@ -9,7 +9,8 @@ readme = "docs/index.md"
9
9
  requires-python = ">=3.11,<4.0"
10
10
  dependencies = [
11
11
  "astral>=3.2",
12
- "config-cli-gui>=0.3.2",
12
+ "config-cli-gui>=0.3.5",
13
+ "ttkbootstrap>=1.20.3",
13
14
  "exifread>=3.5.1",
14
15
  "geopandas>=1.1.1",
15
16
  "holidays>=0.84",
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.0.1'
22
- __version_tuple__ = version_tuple = (1, 0, 1)
21
+ __version__ = version = '1.0.2'
22
+ __version_tuple__ = version_tuple = (1, 0, 2)
23
23
 
24
- __commit_id__ = commit_id = 'gd3847087e'
24
+ __commit_id__ = commit_id = 'gfd5b3ded1'
@@ -69,7 +69,7 @@ class Photo:
69
69
  """Returns an Image object if the file can be opened."""
70
70
  try:
71
71
  return Image.open(self.file_path)
72
- except Exception as e:
72
+ except (OSError, SyntaxError) as e:
73
73
  print(f"Error opening image: {e}")
74
74
  return None
75
75
 
@@ -113,7 +113,7 @@ class Photo:
113
113
 
114
114
  def get_photos_from_dir(
115
115
  image_folder: Path, locations: dict[str, tuple[float, float]] = None
116
- ) -> list["Photo"] | None:
116
+ ) -> list[Photo]:
117
117
  """
118
118
  Reads all image files from a folder and returns a list of Photo objects.
119
119
  """
@@ -129,11 +129,7 @@ def get_photos_from_dir(
129
129
  if file.lower().endswith((".png", ".jpg", ".jpeg"))
130
130
  ]
131
131
 
132
- if not image_files:
133
- print(f"No images found in '{image_folder}'.")
134
- return None
135
-
136
- return [Photo(Path(file), locations) for file in image_files] # Create Photo objects
132
+ return [Photo(Path(file), locations) for file in image_files]
137
133
 
138
134
 
139
135
  def get_photo_dates(photos: list[Photo]) -> str:
@@ -15,20 +15,6 @@ from config_cli_gui.configtypes.font import Font
15
15
  from config_cli_gui.docs import DocumentationGenerator
16
16
 
17
17
 
18
- class AppConfig(ConfigCategory):
19
- """Application-specific configuration parameters."""
20
-
21
- def get_category_name(self) -> str:
22
- return "app"
23
-
24
- log_level: ConfigParameter = ConfigParameter(
25
- name="log_level",
26
- value="INFO",
27
- choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
28
- help="Logging level for the application",
29
- )
30
-
31
-
32
18
  class GeneralConfig(ConfigCategory):
33
19
  """GENERAL configuration parameters."""
34
20
 
@@ -239,6 +225,19 @@ class LayoutConfig(ConfigCategory):
239
225
  help="Spacing between elements in mm",
240
226
  )
241
227
 
228
+ useRoundedCorners: ConfigParameter = ConfigParameter(
229
+ name="useRoundedCorners",
230
+ value=True,
231
+ help="Use rounded corners at the edges of the images in the collage",
232
+ )
233
+
234
+ imageScoreFactor: ConfigParameter = ConfigParameter(
235
+ name="imageScoreFactor",
236
+ value=2.0,
237
+ help="Factor how much the objects (humans, animals, ...) in the image "
238
+ "are considered for weighting the image size",
239
+ )
240
+
242
241
  useShortDayNames: ConfigParameter = ConfigParameter(
243
242
  name="useShortDayNames",
244
243
  value=False,
@@ -274,7 +273,6 @@ class LayoutConfig(ConfigCategory):
274
273
  class ConfigParameterManager(ConfigManager):
275
274
  """Main configuration manager that handles all parameter categories."""
276
275
 
277
- app: AppConfig
278
276
  general: GeneralConfig
279
277
  calendar: CalendarConfig
280
278
  style: StyleConfig
@@ -284,7 +282,6 @@ class ConfigParameterManager(ConfigManager):
284
282
 
285
283
  def __init__(self, config_file: str | None = None, **kwargs):
286
284
  categories = (
287
- AppConfig(),
288
285
  GeneralConfig(),
289
286
  CalendarConfig(),
290
287
  StyleConfig(),
@@ -11,12 +11,17 @@ from config_cli_gui.logging import get_logger, initialize_logging
11
11
  from PIL import Image, ImageDraw
12
12
 
13
13
  from Photo_Composition_Designer.common.Locations import Locations
14
- from Photo_Composition_Designer.common.Photo import Photo, get_photo_dates, get_photos_from_dir
14
+ from Photo_Composition_Designer.common.Photo import (
15
+ Photo,
16
+ get_photo_dates,
17
+ get_photos_from_dir,
18
+ )
15
19
  from Photo_Composition_Designer.config.config import ConfigParameterManager
16
20
  from Photo_Composition_Designer.image.CalendarRenderer import CalendarRenderer
17
21
  from Photo_Composition_Designer.image.CollageRenderer import CollageRenderer
18
22
  from Photo_Composition_Designer.image.DescriptionRenderer import DescriptionRenderer
19
23
  from Photo_Composition_Designer.image.MapRenderer import MapRenderer
24
+ from Photo_Composition_Designer.image.ObjectDetector import ObjectDetector # Import ObjectDetector
20
25
  from Photo_Composition_Designer.tools.Helpers import mm_to_px
21
26
 
22
27
 
@@ -73,6 +78,11 @@ class CompositionDesigner:
73
78
  # colors
74
79
  background_color = self.config.style.backgroundColor.value.to_pil()
75
80
 
81
+ # Create ObjectDetector instance once
82
+ self.object_detector = ObjectDetector() if self.use_object_recognition else None
83
+ if self.object_detector:
84
+ self.object_detector.clear_cache()
85
+
76
86
  # Create other helpers/generators — pass config object for them to pull values from.
77
87
  self.mapGenerator: MapRenderer = MapRenderer.from_config(self.config)
78
88
  self.descGenerator: DescriptionRenderer = DescriptionRenderer.from_config(self.config)
@@ -87,6 +97,9 @@ class CompositionDesigner:
87
97
  self.spacing_px,
88
98
  background_color,
89
99
  self.use_object_recognition,
100
+ self.config.layout.useRoundedCorners.value,
101
+ self.config.layout.imageScoreFactor.value,
102
+ self.object_detector, # Pass the shared ObjectDetector instance
90
103
  )
91
104
  start_date_cfg = self.config.calendar.startDate.value
92
105
  if self.compositionTitle:
@@ -145,7 +158,8 @@ class CompositionDesigner:
145
158
  self.compositionTitle, available_cal_width, self.calendar_height_px
146
159
  )
147
160
  composition.paste(
148
- title_img, (self.margin_sides_px, self.height_px - self.calendar_height_px)
161
+ title_img,
162
+ (self.margin_sides_px, self.height_px - self.calendar_height_px),
149
163
  )
150
164
  elif self.config.calendar.useCalendar.value:
151
165
  if self.config.geo.usePhotoLocationMaps.value:
@@ -229,7 +243,10 @@ class CompositionDesigner:
229
243
  photo_description = [text_file.stem]
230
244
  return photo_description
231
245
 
232
- def generate_compositions_from_folder(self, folder_name: str) -> Image.Image | None:
246
+ def generate_compositions_from_folder(
247
+ self,
248
+ folder_name: str,
249
+ ) -> Image.Image | None:
233
250
  """
234
251
  Generates a single collage for the given folder name.
235
252
  Returns True if a composition was generated, False if skipped.
@@ -301,7 +318,7 @@ class CompositionDesigner:
301
318
  output_file_name = f"{output_prefix}.jpg"
302
319
  output_path = self.outputDir / output_file_name
303
320
  jpg_quality = int(self.config.size.jpgQuality.value)
304
- dpi_tuple = (self.dpi, self.dpi)
321
+ dpi_tuple = (self.dpi, self.dpi) # Use original DPI for saving
305
322
  composition.save(output_path, quality=jpg_quality, dpi=dpi_tuple)
306
323
  self.logger.info(f"Composition saved: {output_path}")
307
324
 
@@ -45,13 +45,19 @@ class GuiLogWriter:
45
45
  tag_name = f"link_{len(self.hyperlink_tags)}"
46
46
  self.text_widget.tag_config(tag_name, foreground="blue", underline=True)
47
47
  self.text_widget.tag_bind(
48
- tag_name, "<Button-1>", lambda e, p=path: self._open_path_in_explorer(p)
48
+ tag_name,
49
+ "<Button-1>",
50
+ lambda e, p=path: self._open_path_in_explorer(p),
49
51
  )
50
52
  self.text_widget.tag_bind(
51
- tag_name, "<Enter>", lambda e: self.text_widget.config(cursor="hand2")
53
+ tag_name,
54
+ "<Enter>",
55
+ lambda e: self.text_widget.config(cursor="hand2"),
52
56
  )
53
57
  self.text_widget.tag_bind(
54
- tag_name, "<Leave>", lambda e: self.text_widget.config(cursor="")
58
+ tag_name,
59
+ "<Leave>",
60
+ lambda e: self.text_widget.config(cursor=""),
55
61
  )
56
62
  self.text_widget.tag_add(tag_name, start_index, end_index)
57
63
  self.hyperlink_tags[tag_name] = path