qcanvas 1.2.0__py3-none-any.whl → 2026.1.19__py3-none-any.whl

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 (132) hide show
  1. qcanvas/__init__.py +60 -0
  2. qcanvas/app.py +72 -0
  3. qcanvas/backend_connectors/frontend_resource_manager.py +13 -5
  4. qcanvas/backend_connectors/qcanvas_task_master.py +2 -2
  5. qcanvas/icons/__init__.py +5 -5
  6. qcanvas/icons/_icon_type.py +1 -1
  7. qcanvas/icons/icons.qrc +39 -35
  8. qcanvas/icons/rc_icons.py +1298 -1197
  9. qcanvas/settings/__init__.py +6 -0
  10. qcanvas/{util/settings → settings}/_client_settings.py +4 -4
  11. qcanvas/settings/_course_settings.py +54 -0
  12. qcanvas/{util/settings → settings}/_mapped_setting.py +2 -5
  13. qcanvas/{util/settings → settings}/_ui_settings.py +5 -5
  14. qcanvas/theme.py +101 -0
  15. qcanvas/ui/course_viewer/content_tree.py +9 -12
  16. qcanvas/ui/course_viewer/course_tree/_course_icon_generator.py +3 -3
  17. qcanvas/ui/course_viewer/course_tree/course_tree.py +9 -8
  18. qcanvas/ui/course_viewer/course_viewer.py +42 -56
  19. qcanvas/ui/course_viewer/tabs/assignment_tab/assignment_tab.py +107 -29
  20. qcanvas/ui/course_viewer/tabs/assignment_tab/assignment_tree.py +4 -4
  21. qcanvas/ui/course_viewer/tabs/constants.py +1 -0
  22. qcanvas/ui/course_viewer/tabs/content_tab.py +33 -39
  23. qcanvas/ui/course_viewer/tabs/file_tab/file_tab.py +4 -4
  24. qcanvas/ui/course_viewer/tabs/file_tab/file_tree.py +7 -10
  25. qcanvas/ui/course_viewer/tabs/file_tab/pages_file_tree.py +6 -7
  26. qcanvas/ui/course_viewer/tabs/mail_tab/mail_tab.py +50 -27
  27. qcanvas/ui/course_viewer/tabs/mail_tab/mail_tree.py +7 -8
  28. qcanvas/ui/course_viewer/tabs/page_tab/page_tab.py +3 -3
  29. qcanvas/ui/course_viewer/tabs/page_tab/page_tree.py +5 -5
  30. qcanvas/ui/course_viewer/tabs/resource_rich_browser.py +18 -32
  31. qcanvas/ui/course_viewer/tree_widget_data_item.py +1 -1
  32. qcanvas/ui/memory_tree/_tree_memory.py +45 -42
  33. qcanvas/ui/memory_tree/memory_tree_widget.py +22 -18
  34. qcanvas/ui/memory_tree/memory_tree_widget_item.py +3 -3
  35. qcanvas/ui/qcanvas_window/__init__.py +1 -0
  36. qcanvas/ui/{main_ui → qcanvas_window}/course_viewer_container.py +10 -10
  37. qcanvas/ui/{main_ui → qcanvas_window}/options/auto_download_resources_option.py +5 -5
  38. qcanvas/ui/{main_ui → qcanvas_window}/options/quick_sync_option.py +7 -6
  39. qcanvas/ui/{main_ui → qcanvas_window}/options/sync_on_start_option.py +7 -6
  40. qcanvas/ui/{main_ui → qcanvas_window}/options/theme_selection_menu.py +10 -10
  41. qcanvas/ui/{main_ui → qcanvas_window}/qcanvas_window.py +57 -41
  42. qcanvas/ui/{main_ui → qcanvas_window}/status_bar_progress_display.py +5 -6
  43. qcanvas/ui/qml_components/__init__.py +4 -0
  44. qcanvas/ui/qml_components/attachments_pane.py +70 -0
  45. qcanvas/ui/qml_components/comments_pane.py +83 -0
  46. qcanvas/ui/qml_components/qml/AttachmentsList.ui.qml +15 -0
  47. qcanvas/ui/qml_components/qml/AttachmentsListDelegate.qml +77 -0
  48. qcanvas/ui/qml_components/qml/AttachmentsListModel.qml +19 -0
  49. qcanvas/ui/qml_components/qml/AttachmentsPane.qml +11 -0
  50. qcanvas/ui/qml_components/qml/CommentsList.ui.qml +15 -0
  51. qcanvas/ui/qml_components/qml/CommentsListDelegate.ui.qml +118 -0
  52. qcanvas/ui/qml_components/qml/CommentsListModel.qml +56 -0
  53. qcanvas/ui/qml_components/qml/CommentsPane.qml +11 -0
  54. qcanvas/ui/qml_components/qml/DecoratedText.ui.qml +44 -0
  55. qcanvas/ui/qml_components/qml/Spacer.ui.qml +7 -0
  56. qcanvas/ui/qml_components/qml/ThemedRectangle.qml +53 -0
  57. qcanvas/ui/qml_components/qml/__init__.py +3 -0
  58. qcanvas/ui/qml_components/qml/rc_qml.py +709 -0
  59. qcanvas/ui/qml_components/qml/rc_qml.qrc +16 -0
  60. qcanvas/ui/qml_components/qml_bridge_types.py +95 -0
  61. qcanvas/ui/qml_components/qml_pane.py +21 -0
  62. qcanvas/ui/setup/setup_checker.py +1 -1
  63. qcanvas/ui/setup/setup_dialog.py +28 -14
  64. qcanvas/util/auto_downloader.py +9 -7
  65. qcanvas/util/basic_fonts.py +2 -2
  66. qcanvas/util/context_dict.py +12 -0
  67. qcanvas/util/file_icons.py +11 -19
  68. qcanvas/util/layouts.py +5 -7
  69. qcanvas/util/paths.py +17 -6
  70. qcanvas/util/qurl_util.py +1 -1
  71. qcanvas/util/ui_tools.py +118 -8
  72. qcanvas/util/url_checker.py +1 -1
  73. qcanvas-2026.1.19.dist-info/METADATA +95 -0
  74. qcanvas-2026.1.19.dist-info/RECORD +92 -0
  75. {qcanvas-1.2.0.dist-info → qcanvas-2026.1.19.dist-info}/WHEEL +1 -1
  76. qcanvas-2026.1.19.dist-info/entry_points.txt +3 -0
  77. qcanvas/app_start/__init__.py +0 -59
  78. qcanvas/icons/_update_icons.py +0 -89
  79. qcanvas/icons/dark/actions/exit.svg +0 -3
  80. qcanvas/icons/dark/actions/mark_all_read.svg +0 -3
  81. qcanvas/icons/dark/actions/open_downloads.svg +0 -3
  82. qcanvas/icons/dark/actions/quick_login.svg +0 -3
  83. qcanvas/icons/dark/actions/sync.svg +0 -3
  84. qcanvas/icons/dark/branding/logo_transparent.svg +0 -303
  85. qcanvas/icons/dark/options/auto_download.svg +0 -3
  86. qcanvas/icons/dark/options/theme.svg +0 -3
  87. qcanvas/icons/dark/tabs/assignments.svg +0 -3
  88. qcanvas/icons/dark/tabs/mail.svg +0 -3
  89. qcanvas/icons/dark/tabs/pages.svg +0 -3
  90. qcanvas/icons/dark/tree_items/assignment.svg +0 -3
  91. qcanvas/icons/dark/tree_items/mail.svg +0 -3
  92. qcanvas/icons/dark/tree_items/module.svg +0 -3
  93. qcanvas/icons/dark/tree_items/page.svg +0 -3
  94. qcanvas/icons/light/actions/exit.svg +0 -3
  95. qcanvas/icons/light/actions/mark_all_read.svg +0 -3
  96. qcanvas/icons/light/actions/open_downloads.svg +0 -3
  97. qcanvas/icons/light/actions/quick_login.svg +0 -3
  98. qcanvas/icons/light/actions/sync.svg +0 -3
  99. qcanvas/icons/light/branding/logo_transparent.svg +0 -304
  100. qcanvas/icons/light/options/auto_download.svg +0 -3
  101. qcanvas/icons/light/options/ignore_old.svg +0 -3
  102. qcanvas/icons/light/options/include_videos.svg +0 -3
  103. qcanvas/icons/light/options/theme.svg +0 -3
  104. qcanvas/icons/light/tabs/assignments.svg +0 -3
  105. qcanvas/icons/light/tabs/mail.svg +0 -3
  106. qcanvas/icons/light/tabs/pages.svg +0 -3
  107. qcanvas/icons/light/tree_items/assignment.svg +0 -3
  108. qcanvas/icons/light/tree_items/mail.svg +0 -3
  109. qcanvas/icons/light/tree_items/module.svg +0 -3
  110. qcanvas/icons/light/tree_items/page.svg +0 -3
  111. qcanvas/icons/universal/branding/main_icon.svg +0 -325
  112. qcanvas/icons/universal/downloads/download_failed.svg +0 -23
  113. qcanvas/icons/universal/downloads/downloaded.svg +0 -23
  114. qcanvas/icons/universal/downloads/not_downloaded.svg +0 -23
  115. qcanvas/icons/universal/downloads/unknown.svg +0 -6
  116. qcanvas/icons/universal/tabs/assignments_new_content.svg +0 -3
  117. qcanvas/icons/universal/tabs/mail_new_content.svg +0 -3
  118. qcanvas/icons/universal/tabs/pages_new_content.svg +0 -3
  119. qcanvas/icons/universal/tree_items/semester.svg +0 -108
  120. qcanvas/run.py +0 -54
  121. qcanvas/ui/course_viewer/tabs/util.py +0 -11
  122. qcanvas/ui/main_ui/__init__.py +0 -0
  123. qcanvas/util/settings/__init__.py +0 -9
  124. qcanvas/util/themes/__init__.py +0 -2
  125. qcanvas/util/themes/_colour_scheme_helper.py +0 -38
  126. qcanvas/util/themes/_selected_theme.py +0 -10
  127. qcanvas/util/themes/_theme_changed_event.py +0 -17
  128. qcanvas/util/themes/_theme_changer.py +0 -86
  129. qcanvas-1.2.0.dist-info/METADATA +0 -71
  130. qcanvas-1.2.0.dist-info/RECORD +0 -118
  131. qcanvas-1.2.0.dist-info/entry_points.txt +0 -3
  132. /qcanvas/ui/{main_ui → qcanvas_window}/options/__init__.py +0 -0
@@ -2,25 +2,23 @@ import html
2
2
  import logging
3
3
  from typing import Optional
4
4
 
5
- import qcanvas_backend.database.types as db
5
+ from libqcanvas import db
6
6
  from bs4 import BeautifulSoup, Tag
7
+ from libqcanvas.net.resources.extracting.no_extractor_error import NoExtractorError
8
+ from libqcanvas.util import is_link_invisible
9
+ from PySide6.QtCore import QUrl, Slot
10
+ from PySide6.QtGui import QDesktopServices
11
+ from PySide6.QtWidgets import QTextBrowser
7
12
  from qasync import asyncSlot
8
- from qcanvas_backend.net.resources.download.resource_manager import ResourceManager
9
- from qcanvas_backend.net.resources.extracting.no_extractor_error import NoExtractorError
10
- from qcanvas_backend.util import is_link_invisible
11
- from qtpy.QtCore import QUrl, Slot
12
- from qtpy.QtGui import QDesktopServices
13
- from qtpy.QtWidgets import QTextBrowser
14
13
 
15
14
  from qcanvas.backend_connectors import FrontendResourceManager
16
15
  from qcanvas.util.html_cleaner import clean_up_html
17
- from qcanvas.util.qurl_util import file_url
18
16
 
19
17
  _logger = logging.getLogger(__name__)
20
18
 
21
19
 
22
20
  class ResourceRichBrowser(QTextBrowser):
23
- def __init__(self, downloader: ResourceManager):
21
+ def __init__(self, downloader: FrontendResourceManager):
24
22
  super().__init__()
25
23
  self._downloader = downloader
26
24
  self._content: Optional[db.CourseContentItem] = None
@@ -30,9 +28,8 @@ class ResourceRichBrowser(QTextBrowser):
30
28
  self.setOpenLinks(False)
31
29
  self.anchorClicked.connect(self._open_url)
32
30
 
33
- if isinstance(self._downloader, FrontendResourceManager):
34
- self._downloader.download_finished.connect(self._download_updated)
35
- self._downloader.download_failed.connect(self._download_updated)
31
+ self._downloader.download_finished.connect(self._download_updated)
32
+ self._downloader.download_failed.connect(self._download_updated)
36
33
 
37
34
  def show_blank(self, completely_blank: bool = False) -> None:
38
35
  if completely_blank:
@@ -43,14 +40,14 @@ class ResourceRichBrowser(QTextBrowser):
43
40
  self._content = None
44
41
  self._current_content_resources.clear()
45
42
 
46
- def show_content(self, page: db.CourseContentItem) -> None:
43
+ def show_content(self, page: db.AnyContentItem) -> None:
47
44
  if page.body is None:
48
45
  self.show_blank()
49
46
  else:
50
47
  self._collect_resources(page)
51
48
  self._show_page_content(page)
52
49
 
53
- def _collect_resources(self, page: db.CourseContentItem):
50
+ def _collect_resources(self, page: db.AnyContentItem):
54
51
  self._current_content_resources = {
55
52
  resource.id: resource for resource in page.resources
56
53
  }
@@ -105,17 +102,9 @@ class ResourceRichBrowser(QTextBrowser):
105
102
 
106
103
  return BeautifulSoup(
107
104
  markup=f"""
108
- <a href="data:{html.escape(resource_id)}" style="font-weight: normal">
109
- <!--<table style="vertical-align: middle; border-collapse: collapse;">
110
- <tr>
111
- <td style="text-decoration: none;">-->
112
- <img height="16" src="{html.escape(self._download_state_icon(resource.download_state))}"/>
113
- <!--</td>
114
- <td>-->
115
- {html.escape(resource.file_name)}
116
- <!--</td>
117
- </tr>
118
- </table>-->
105
+ <a href="data:{html.escape(resource_id)}" style="font-weight: normal;">
106
+ <img height="18" src="{html.escape(self._download_state_icon(resource.download_state))}"/>
107
+ {html.escape(resource.file_name)}
119
108
  </a>
120
109
  """,
121
110
  features="html.parser",
@@ -145,16 +134,13 @@ class ResourceRichBrowser(QTextBrowser):
145
134
  resource = self._current_content_resources[resource_id]
146
135
 
147
136
  try:
148
- await self._downloader.download(resource)
137
+ await self._downloader.download_and_open(resource)
149
138
  except Exception as e:
150
139
  _logger.warning(
151
140
  "Download of resource id=%s failed", resource_id, exc_info=e
152
141
  )
153
142
  return
154
143
 
155
- resource_path = file_url(self._downloader.resource_download_location(resource))
156
- QDesktopServices.openUrl(resource_path)
157
-
158
144
  @Slot(db.Resource)
159
145
  def _download_updated(self, resource: db.Resource) -> None:
160
146
  if self._content is not None and resource.id in self._current_content_resources:
@@ -171,8 +157,8 @@ class ResourceRichBrowser(QTextBrowser):
171
157
  _logger.warning(
172
158
  "Resource has diverged from current loaded data, applying bandaid fix"
173
159
  )
174
- self._current_content_resources[resource.id].download_state = (
175
- resource.download_state
176
- )
160
+ self._current_content_resources[
161
+ resource.id
162
+ ].download_state = resource.download_state
177
163
 
178
164
  self._show_page_content(self._content)
@@ -1,6 +1,6 @@
1
1
  from typing import List, Optional
2
2
 
3
- from qtpy.QtWidgets import QTreeWidgetItem
3
+ from PySide6.QtWidgets import QTreeWidgetItem
4
4
 
5
5
  from qcanvas.ui.memory_tree import MemoryTreeWidgetItem
6
6
 
@@ -1,49 +1,41 @@
1
+ import asyncio
1
2
  import logging
2
3
  from pathlib import Path
3
- from typing import *
4
4
 
5
- from lightdb import LightDB, Model
5
+ from aiofile import async_open
6
6
 
7
7
  from qcanvas.util import paths
8
8
 
9
9
  _logger = logging.getLogger(__name__)
10
10
 
11
11
 
12
- def _storage_path() -> Path:
13
- path = paths.ui_storage() / "TREE.DB"
14
- path.parent.mkdir(parents=True, exist_ok=True)
15
- return path
16
-
17
-
18
- _state_db = LightDB(str(_storage_path()))
19
-
20
-
21
- class _TreeState(Model, table="trees", db=_state_db):
22
- tree_name: str
23
- collapsed_items: List[str] = []
24
-
12
+ class TreeMemory:
13
+ def __init__(self, tree_name: str):
14
+ self._tree_name = tree_name
15
+ self._loaded = False
16
+ self._collapsed_items: set[str] = set()
25
17
 
26
- def _get_or_create_state(name: str) -> _TreeState:
27
- state = _TreeState.get(tree_name=name)
18
+ def load(self, force: bool = False):
19
+ if force or not self._loaded:
20
+ self._loaded = True
28
21
 
29
- if state is None:
30
- state = _TreeState.create(tree_name=name)
31
- # Initialise the list here! Or else every instance has the same list object
32
- state.collapsed_items = []
33
- # Important or instances will get duplicated data in some cases
34
- state.save()
35
- return state
36
- else:
37
- return state
22
+ if not self._storage_path.exists():
23
+ # Nothing to do
24
+ return
38
25
 
26
+ # fixme this blocks the event loop, but using aiofile will require significant changes to other
27
+ # parts of the code to accommodate these methods now being async.
28
+ # this will really only have any noticeable effect on slow disks, and only ever happens once anyway.
29
+ with open(self._storage_path, "rt") as file:
30
+ lines = file.read().splitlines()
31
+ _logger.debug("Tree % loaded %s", self._tree_name, lines)
32
+ self._collapsed_items.update(lines)
39
33
 
40
- class TreeMemory:
41
- def __init__(self, tree_name: str):
42
- self._tree_name = tree_name
43
- self._state = _get_or_create_state(tree_name)
34
+ async def save(self):
35
+ assert self._loaded, "Memory is not loaded yet"
44
36
 
45
- def is_expanded(self, node_id: str) -> bool:
46
- return node_id in self._state.expanded_items
37
+ async with async_open(self._storage_path, "wt") as file:
38
+ await file.write("\n".join(self._collapsed_items))
47
39
 
48
40
  def expanded(self, node_id: str) -> None:
49
41
  self.set_expanded(node_id, True)
@@ -52,16 +44,27 @@ class TreeMemory:
52
44
  self.set_expanded(node_id, False)
53
45
 
54
46
  def set_expanded(self, node_id: str, expanded: bool) -> None:
55
- contains = node_id in self._state.collapsed_items
47
+ self.load()
56
48
 
57
- # fixme when using a slow usb stick, this can momentarily block the event loop.
58
- if expanded and contains:
59
- self._state.collapsed_items.remove(node_id)
60
- self._state.save()
61
- elif not expanded and not contains:
62
- self._state.collapsed_items.append(node_id)
63
- self._state.save()
49
+ if expanded and node_id in self._collapsed_items:
50
+ self._collapsed_items.remove(node_id)
51
+ else:
52
+ self._collapsed_items.add(node_id)
53
+
54
+ # hack: avoid blocking the event loop
55
+ asyncio.create_task(self.save())
56
+
57
+ @property
58
+ def collapsed_ids(self) -> set[str]:
59
+ assert self._loaded, "Memory not loaded yet"
60
+ return self._collapsed_items
64
61
 
65
62
  @property
66
- def collapsed_ids(self) -> List[str]:
67
- return self._state.collapsed_items
63
+ def _storage_path(self) -> Path:
64
+ path = (
65
+ paths.data_storage()
66
+ / "tree_state"
67
+ / f"{self._tree_name}_collapsed_items.txt"
68
+ )
69
+ path.parent.mkdir(parents=True, exist_ok=True)
70
+ return path
@@ -1,8 +1,8 @@
1
1
  import logging
2
- from typing import *
2
+ from typing import Optional, Sequence
3
3
 
4
- from qtpy.QtCore import QItemSelectionModel, Slot
5
- from qtpy.QtWidgets import QTreeWidget, QTreeWidgetItem, QWidget
4
+ from PySide6.QtCore import QItemSelectionModel, Slot
5
+ from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem, QWidget
6
6
 
7
7
  from qcanvas.ui.memory_tree._tree_memory import TreeMemory
8
8
  from qcanvas.ui.memory_tree.memory_tree_widget_item import MemoryTreeWidgetItem
@@ -10,6 +10,10 @@ from qcanvas.ui.memory_tree.memory_tree_widget_item import MemoryTreeWidgetItem
10
10
  _logger = logging.getLogger(__name__)
11
11
 
12
12
 
13
+ class _HasID:
14
+ id: str
15
+
16
+
13
17
  class MemoryTreeWidget(QTreeWidget):
14
18
  def __init__(
15
19
  self,
@@ -17,7 +21,7 @@ class MemoryTreeWidget(QTreeWidget):
17
21
  parent: Optional[QWidget] = None,
18
22
  ):
19
23
  super().__init__(parent)
20
- self._id_map: dict[str, QTreeWidgetItem] = {}
24
+ self._id_map: dict[str, QTreeWidgetItem | _HasID] = {}
21
25
  self._memory = TreeMemory(tree_name)
22
26
  self._suppress_expansion_signals = False
23
27
  self._suppress_selection_signal = False
@@ -30,7 +34,7 @@ class MemoryTreeWidget(QTreeWidget):
30
34
 
31
35
  try:
32
36
  self._suppress_expansion_signals = True
33
-
37
+ self._memory.load()
34
38
  collapsed_ids = self._memory.collapsed_ids
35
39
 
36
40
  for widget in self._id_map.values():
@@ -44,7 +48,7 @@ class MemoryTreeWidget(QTreeWidget):
44
48
  super().clear()
45
49
  self._id_map.clear()
46
50
 
47
- def select_ids(self, ids: List[str]) -> bool:
51
+ def select_ids(self, ids: list[str]) -> bool:
48
52
  """
49
53
  :returns: True if all ids were still found in the tree, False if one or more was missing
50
54
  """
@@ -94,26 +98,26 @@ class MemoryTreeWidget(QTreeWidget):
94
98
  def _add_widget_to_id_map(
95
99
  self, widget: QTreeWidgetItem | Sequence[QTreeWidgetItem]
96
100
  ):
97
- map_updates = {}
98
- widget_stack = widget if isinstance(widget, List) else [widget]
101
+ widget_stack = widget if isinstance(widget, list) else [widget]
99
102
 
100
103
  while len(widget_stack) > 0:
101
- item = widget_stack.pop()
104
+ popped_widget = widget_stack.pop()
102
105
 
103
- if hasattr(item, "id"):
104
- if item.id in self._id_map or item.id in map_updates:
105
- raise ValueError(f"Item with ID {item.id} is already in the tree")
106
+ if hasattr(popped_widget, "id"):
107
+ if popped_widget.id in self._id_map:
108
+ raise ValueError(
109
+ f"Item with ID {popped_widget.id} is already in the tree"
110
+ )
106
111
 
107
- map_updates[item.id] = item
108
- _logger.debug("Add %s to map", item.id)
112
+ self._id_map[popped_widget.id] = popped_widget
113
+ _logger.debug("Add %s to map", popped_widget.id)
109
114
 
110
- if item.childCount() > 0:
115
+ if popped_widget.childCount() > 0:
111
116
  widget_stack.extend(
112
- [item.child(index) for index in range(0, item.childCount())]
117
+ popped_widget.child(index)
118
+ for index in range(0, popped_widget.childCount())
113
119
  )
114
120
 
115
- self._id_map.update(map_updates.items())
116
-
117
121
  @Slot(QTreeWidgetItem)
118
122
  def _expanded(self, item: QTreeWidgetItem):
119
123
  if self._suppress_expansion_signals:
@@ -1,14 +1,14 @@
1
1
  import logging
2
- from typing import *
2
+ from typing import Optional
3
3
 
4
- from qtpy.QtWidgets import QTreeWidgetItem
4
+ from PySide6.QtWidgets import QTreeWidgetItem
5
5
 
6
6
  _logger = logging.getLogger(__name__)
7
7
 
8
8
 
9
9
  class MemoryTreeWidgetItem(QTreeWidgetItem):
10
10
  def __init__(
11
- self, id: str, data: Optional[object], strings: Optional[List[str]] = None
11
+ self, id: str, data: Optional[object], strings: Optional[list[str]] = None
12
12
  ):
13
13
  super().__init__(strings)
14
14
  self._id = id
@@ -0,0 +1 @@
1
+ from .qcanvas_window import QCanvasWindow
@@ -1,16 +1,16 @@
1
1
  import logging
2
2
  from math import floor
3
- from typing import *
3
+ from typing import Optional, Sequence
4
4
 
5
- import qcanvas_backend.database.types as db
6
- from qcanvas_backend.net.resources.download.resource_manager import ResourceManager
7
- from qcanvas_backend.net.sync.sync_receipt import SyncReceipt, empty_receipt
8
- from qtpy.QtCore import Qt, Slot
9
- from qtpy.QtWidgets import *
5
+ from libqcanvas import db
6
+ from libqcanvas.net.resources.download.resource_manager import ResourceManager
7
+ from libqcanvas.net.sync.sync_receipt import SyncReceipt, empty_receipt
8
+ from PySide6.QtCore import Qt, Slot
9
+ from PySide6.QtWidgets import QLabel, QSizePolicy, QStackedWidget
10
10
 
11
11
  from qcanvas import icons
12
12
  from qcanvas.ui.course_viewer.course_viewer import CourseViewer
13
- from qcanvas.util import themes
13
+ from qcanvas.theme import app_theme
14
14
 
15
15
  _logger = logging.getLogger(__name__)
16
16
 
@@ -29,17 +29,17 @@ class _PlaceholderLogo(QLabel):
29
29
  self.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Ignored)
30
30
  # Because we are using a pixmap for the icon, it will not get updated like a normal QIcon when the theme changes,
31
31
  # So we need to update it ourselves
32
- themes.theme_changed().connect(self._theme_changed)
32
+ app_theme.darkModeChanged.connect(self._dark_mode_changed)
33
33
 
34
34
  def resizeEvent(self, event) -> None:
35
35
  self._update_image()
36
36
 
37
37
  @Slot()
38
- def _theme_changed(self) -> None:
38
+ def _dark_mode_changed(self) -> None:
39
39
  self._update_image(force=True)
40
40
 
41
41
  def _update_image(self, force: bool = False) -> None:
42
- # Calculate the size of the logo as half of the width/height with a max size of 1000x1000
42
+ # Calculate the size of the logo as half of the width/height, but prevent it from becoming too large
43
43
  width = min(floor(self.width() * 0.5), 500)
44
44
  height = min(floor(self.height() * 0.5), 500)
45
45
 
@@ -1,12 +1,12 @@
1
1
  import logging
2
- from typing import *
2
+ from typing import Optional
3
3
 
4
- from qtpy.QtCore import Slot
5
- from qtpy.QtGui import QAction
6
- from qtpy.QtWidgets import QMenu
4
+ from PySide6.QtCore import Slot
5
+ from PySide6.QtGui import QAction
6
+ from PySide6.QtWidgets import QMenu
7
7
 
8
8
  from qcanvas import icons
9
- from qcanvas.util import settings
9
+ import qcanvas.settings as settings
10
10
 
11
11
  _logger = logging.getLogger(__name__)
12
12
 
@@ -1,11 +1,11 @@
1
1
  import logging
2
- from typing import *
2
+ from typing import Optional
3
3
 
4
- from qtpy.QtCore import Slot
5
- from qtpy.QtGui import QAction
6
- from qtpy.QtWidgets import QMenu
4
+ from PySide6.QtCore import Slot
5
+ from PySide6.QtGui import QAction
6
+ from PySide6.QtWidgets import QMenu
7
7
 
8
- from qcanvas.util import settings
8
+ import qcanvas.settings as settings
9
9
 
10
10
  _logger = logging.getLogger(__name__)
11
11
 
@@ -14,11 +14,12 @@ class QuickSyncOption(QAction):
14
14
  def __init__(self, parent: Optional[QMenu] = None):
15
15
  super().__init__("Ignore old courses", parent)
16
16
  self.setToolTip(
17
- "When this option is selected, old courses will not be synchronised. This will only effect the first sync."
17
+ "When enabled, old courses will not be synchronised. This won't hide any old courses that were previously synchronised."
18
18
  )
19
19
  self.setCheckable(True)
20
20
  self.setChecked(settings.client.quick_sync_enabled)
21
21
  self.triggered.connect(self._triggered)
22
+ # self.setIcon(icons.options.ignore_old)
22
23
 
23
24
  @Slot()
24
25
  def _triggered(self) -> None:
@@ -1,11 +1,11 @@
1
1
  import logging
2
- from typing import *
2
+ from typing import Optional
3
3
 
4
- from qtpy.QtCore import Slot
5
- from qtpy.QtGui import QAction
6
- from qtpy.QtWidgets import QMenu
4
+ from PySide6.QtCore import Slot
5
+ from PySide6.QtGui import QAction
6
+ from PySide6.QtWidgets import QMenu
7
7
 
8
- from qcanvas.util import settings
8
+ import qcanvas.settings as settings
9
9
 
10
10
  _logger = logging.getLogger(__name__)
11
11
 
@@ -14,11 +14,12 @@ class SyncOnStartOption(QAction):
14
14
  def __init__(self, parent: Optional[QMenu] = None):
15
15
  super().__init__("Sync on start", parent)
16
16
  self.setToolTip(
17
- "When this option is selected, synchronisation will be started automatically when the app starts."
17
+ "When enabled, synchronisation will start when the application is opened."
18
18
  )
19
19
  self.setCheckable(True)
20
20
  self.setChecked(settings.client.sync_on_start)
21
21
  self.triggered.connect(self._triggered)
22
+ # self.set(icons.options.sync_on_start)
22
23
 
23
24
  @Slot()
24
25
  def _triggered(self) -> None:
@@ -1,11 +1,12 @@
1
1
  import logging
2
2
 
3
- from qtpy.QtCore import Slot
4
- from qtpy.QtGui import QAction, QActionGroup
5
- from qtpy.QtWidgets import QMenu
3
+ from PySide6.QtCore import Slot
4
+ from PySide6.QtGui import QAction, QActionGroup
5
+ from PySide6.QtWidgets import QMenu
6
6
 
7
7
  from qcanvas import icons
8
- from qcanvas.util import settings, themes
8
+ from qcanvas.theme import app_theme
9
+ import qcanvas.settings as settings
9
10
 
10
11
  _logger = logging.getLogger(__name__)
11
12
 
@@ -21,11 +22,10 @@ class _ThemeAction(QAction):
21
22
  @Slot()
22
23
  def _change_theme(self) -> None:
23
24
  settings.ui.theme = self._theme_name
24
- themes.apply(self._theme_name)
25
+ app_theme.theme = self._theme_name
25
26
 
26
27
 
27
28
  class ThemeSelectionMenu(QMenu):
28
-
29
29
  def __init__(self, parent: QMenu):
30
30
  super().__init__("Theme", parent)
31
31
 
@@ -36,10 +36,10 @@ class ThemeSelectionMenu(QMenu):
36
36
  dark_theme = _ThemeAction("Dark", "dark", self)
37
37
  native_theme = _ThemeAction("Native (requires restart)", "native", self)
38
38
 
39
- actions = [auto_theme, light_theme, dark_theme, native_theme]
39
+ select_theme_actions = [auto_theme, light_theme, dark_theme, native_theme]
40
40
 
41
- self.addActions(actions)
41
+ self.addActions(select_theme_actions)
42
42
  self.setIcon(icons.options.theme)
43
43
 
44
- for theme in actions:
45
- action_group.addAction(theme)
44
+ for selection_action in select_theme_actions:
45
+ action_group.addAction(selection_action)