qcanvas 1.2.2__py3-none-any.whl → 2.0.1__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.

Potentially problematic release.


This version of qcanvas might be problematic. Click here for more details.

Files changed (128) hide show
  1. qcanvas/__init__.py +54 -0
  2. qcanvas/app.py +93 -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 +47 -43
  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 +4 -4
  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 +35 -43
  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 +15 -21
  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 +38 -33
  42. qcanvas/ui/{main_ui → qcanvas_window}/status_bar_progress_display.py +5 -6
  43. qcanvas/ui/qml_components/AttachmentsList.ui.qml +15 -0
  44. qcanvas/ui/qml_components/AttachmentsListDelegate.qml +77 -0
  45. qcanvas/ui/qml_components/AttachmentsListModel.qml +19 -0
  46. qcanvas/ui/qml_components/AttachmentsPane.qml +11 -0
  47. qcanvas/ui/qml_components/CommentsList.ui.qml +15 -0
  48. qcanvas/ui/qml_components/CommentsListDelegate.ui.qml +118 -0
  49. qcanvas/ui/qml_components/CommentsListModel.qml +56 -0
  50. qcanvas/ui/qml_components/CommentsPane.qml +11 -0
  51. qcanvas/ui/qml_components/DecoratedText.ui.qml +44 -0
  52. qcanvas/ui/qml_components/Spacer.ui.qml +7 -0
  53. qcanvas/ui/qml_components/ThemedRectangle.qml +53 -0
  54. qcanvas/ui/qml_components/__init__.py +3 -0
  55. qcanvas/ui/qml_components/attachments_pane.py +72 -0
  56. qcanvas/ui/qml_components/comments_pane.py +85 -0
  57. qcanvas/ui/qml_components/qml_bridge_types.py +95 -0
  58. qcanvas/ui/qml_components/qml_pane.py +22 -0
  59. qcanvas/ui/setup/setup_checker.py +1 -1
  60. qcanvas/ui/setup/setup_dialog.py +27 -10
  61. qcanvas/util/auto_downloader.py +9 -7
  62. qcanvas/util/basic_fonts.py +2 -2
  63. qcanvas/util/context_dict.py +12 -0
  64. qcanvas/util/file_icons.py +11 -19
  65. qcanvas/util/layouts.py +5 -7
  66. qcanvas/util/paths.py +17 -6
  67. qcanvas/util/qurl_util.py +1 -1
  68. qcanvas/util/ui_tools.py +118 -8
  69. qcanvas/util/url_checker.py +1 -1
  70. {qcanvas-1.2.2.dist-info → qcanvas-2.0.1.dist-info}/METADATA +17 -14
  71. qcanvas-2.0.1.dist-info/RECORD +89 -0
  72. {qcanvas-1.2.2.dist-info → qcanvas-2.0.1.dist-info}/WHEEL +1 -1
  73. qcanvas-2.0.1.dist-info/entry_points.txt +3 -0
  74. qcanvas/app_start/__init__.py +0 -59
  75. qcanvas/icons/_update_icons.py +0 -89
  76. qcanvas/icons/dark/actions/exit.svg +0 -3
  77. qcanvas/icons/dark/actions/mark_all_read.svg +0 -3
  78. qcanvas/icons/dark/actions/open_downloads.svg +0 -3
  79. qcanvas/icons/dark/actions/quick_login.svg +0 -3
  80. qcanvas/icons/dark/actions/sync.svg +0 -3
  81. qcanvas/icons/dark/branding/logo_transparent.svg +0 -303
  82. qcanvas/icons/dark/options/auto_download.svg +0 -3
  83. qcanvas/icons/dark/options/theme.svg +0 -3
  84. qcanvas/icons/dark/tabs/assignments.svg +0 -3
  85. qcanvas/icons/dark/tabs/mail.svg +0 -3
  86. qcanvas/icons/dark/tabs/pages.svg +0 -3
  87. qcanvas/icons/dark/tree_items/assignment.svg +0 -3
  88. qcanvas/icons/dark/tree_items/mail.svg +0 -3
  89. qcanvas/icons/dark/tree_items/module.svg +0 -3
  90. qcanvas/icons/dark/tree_items/page.svg +0 -3
  91. qcanvas/icons/light/actions/exit.svg +0 -3
  92. qcanvas/icons/light/actions/mark_all_read.svg +0 -3
  93. qcanvas/icons/light/actions/open_downloads.svg +0 -3
  94. qcanvas/icons/light/actions/quick_login.svg +0 -3
  95. qcanvas/icons/light/actions/sync.svg +0 -3
  96. qcanvas/icons/light/branding/logo_transparent.svg +0 -304
  97. qcanvas/icons/light/options/auto_download.svg +0 -3
  98. qcanvas/icons/light/options/ignore_old.svg +0 -3
  99. qcanvas/icons/light/options/include_videos.svg +0 -3
  100. qcanvas/icons/light/options/theme.svg +0 -3
  101. qcanvas/icons/light/tabs/assignments.svg +0 -3
  102. qcanvas/icons/light/tabs/mail.svg +0 -3
  103. qcanvas/icons/light/tabs/pages.svg +0 -3
  104. qcanvas/icons/light/tree_items/assignment.svg +0 -3
  105. qcanvas/icons/light/tree_items/mail.svg +0 -3
  106. qcanvas/icons/light/tree_items/module.svg +0 -3
  107. qcanvas/icons/light/tree_items/page.svg +0 -3
  108. qcanvas/icons/universal/branding/main_icon.svg +0 -325
  109. qcanvas/icons/universal/downloads/download_failed.svg +0 -23
  110. qcanvas/icons/universal/downloads/downloaded.svg +0 -23
  111. qcanvas/icons/universal/downloads/not_downloaded.svg +0 -23
  112. qcanvas/icons/universal/downloads/unknown.svg +0 -6
  113. qcanvas/icons/universal/tabs/assignments_new_content.svg +0 -3
  114. qcanvas/icons/universal/tabs/mail_new_content.svg +0 -3
  115. qcanvas/icons/universal/tabs/pages_new_content.svg +0 -3
  116. qcanvas/icons/universal/tree_items/semester.svg +0 -108
  117. qcanvas/run.py +0 -54
  118. qcanvas/ui/course_viewer/tabs/util.py +0 -11
  119. qcanvas/ui/main_ui/__init__.py +0 -0
  120. qcanvas/util/settings/__init__.py +0 -9
  121. qcanvas/util/themes/__init__.py +0 -2
  122. qcanvas/util/themes/_colour_scheme_helper.py +0 -38
  123. qcanvas/util/themes/_selected_theme.py +0 -10
  124. qcanvas/util/themes/_theme_changed_event.py +0 -17
  125. qcanvas/util/themes/_theme_changer.py +0 -86
  126. qcanvas-1.2.2.dist-info/RECORD +0 -118
  127. qcanvas-1.2.2.dist-info/entry_points.txt +0 -3
  128. /qcanvas/ui/{main_ui → qcanvas_window}/options/__init__.py +0 -0
@@ -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)
@@ -1,29 +1,39 @@
1
1
  import logging
2
2
  from threading import BoundedSemaphore
3
- from typing import *
3
+ from typing import Optional, Sequence
4
4
 
5
5
  import httpx
6
- import qcanvas_backend.database.types as db
6
+ from libqcanvas import db
7
+ from libqcanvas.database.data_monolith import DataMonolith
8
+ from libqcanvas.net.sync.sync_receipt import SyncReceipt, empty_receipt
9
+ from libqcanvas.qcanvas import QCanvas
10
+ from PySide6.QtCore import QUrl, Signal, Slot, Qt
11
+ from PySide6.QtGui import QDesktopServices, QKeySequence
12
+ from PySide6.QtWidgets import (
13
+ QErrorMessage,
14
+ QHBoxLayout,
15
+ QMainWindow,
16
+ QProgressDialog,
17
+ QPushButton,
18
+ QVBoxLayout,
19
+ QWidget,
20
+ )
7
21
  from qasync import asyncSlot
8
- from qcanvas_backend.database.data_monolith import DataMonolith
9
- from qcanvas_backend.net.sync.sync_receipt import SyncReceipt, empty_receipt
10
- from qcanvas_backend.qcanvas import QCanvas
11
- from qtpy.QtCore import QUrl, Signal, Slot
12
- from qtpy.QtGui import QDesktopServices, QKeySequence
13
- from qtpy.QtWidgets import *
14
22
 
15
23
  from qcanvas import icons
16
24
  from qcanvas.backend_connectors import FrontendResourceManager
25
+ from qcanvas.settings import course_configs
17
26
  from qcanvas.ui.course_viewer import CourseTree
18
- from qcanvas.ui.main_ui.course_viewer_container import CourseViewerContainer
19
- from qcanvas.ui.main_ui.options.auto_download_resources_option import (
27
+ from .course_viewer_container import CourseViewerContainer
28
+ from .options.auto_download_resources_option import (
20
29
  AutoDownloadResourcesMenu,
21
30
  )
22
- from qcanvas.ui.main_ui.options.quick_sync_option import QuickSyncOption
23
- from qcanvas.ui.main_ui.options.sync_on_start_option import SyncOnStartOption
24
- from qcanvas.ui.main_ui.options.theme_selection_menu import ThemeSelectionMenu
25
- from qcanvas.ui.main_ui.status_bar_progress_display import StatusBarProgressDisplay
26
- from qcanvas.util import auto_downloader, paths, settings
31
+ from .options.quick_sync_option import QuickSyncOption
32
+ from .options.sync_on_start_option import SyncOnStartOption
33
+ from .options.theme_selection_menu import ThemeSelectionMenu
34
+ from .status_bar_progress_display import StatusBarProgressDisplay
35
+ from qcanvas.util import auto_downloader
36
+ import qcanvas.settings as settings
27
37
  from qcanvas.util.qurl_util import file_url
28
38
  from qcanvas.util.ui_tools import create_qaction
29
39
 
@@ -33,7 +43,7 @@ _logger = logging.getLogger(__name__)
33
43
  class QCanvasWindow(QMainWindow):
34
44
  _loaded = Signal()
35
45
 
36
- def __init__(self):
46
+ def __init__(self, _qcanvas: QCanvas[FrontendResourceManager]):
37
47
  super().__init__()
38
48
 
39
49
  self.setWindowTitle("QCanvas")
@@ -41,12 +51,7 @@ class QCanvasWindow(QMainWindow):
41
51
 
42
52
  self._operation_semaphore = BoundedSemaphore()
43
53
  self._data: Optional[DataMonolith] = None
44
- self._qcanvas = QCanvas[FrontendResourceManager](
45
- canvas_config=settings.client.canvas_config,
46
- panopto_config=settings.client.panopto_config,
47
- storage_path=paths.data_storage(),
48
- resource_manager_class=FrontendResourceManager,
49
- )
54
+ self._qcanvas = _qcanvas
50
55
 
51
56
  self._course_tree = CourseTree()
52
57
  self._course_tree.item_selected.connect(self._on_course_selected)
@@ -62,7 +67,9 @@ class QCanvasWindow(QMainWindow):
62
67
  self._setup_menu_bar()
63
68
  self._restore_window_position()
64
69
 
65
- self._loaded.connect(self._on_app_loaded)
70
+ self._loaded.connect(
71
+ self._on_app_loaded, Qt.ConnectionType.SingleShotConnection
72
+ )
66
73
  self._loaded.emit()
67
74
 
68
75
  def _setup_menu_bar(self) -> None:
@@ -109,7 +116,7 @@ class QCanvasWindow(QMainWindow):
109
116
  )
110
117
 
111
118
  options_menu = menu_bar.addMenu("Options")
112
-
119
+ options_menu.setToolTipsVisible(True)
113
120
  options_menu.addAction(QuickSyncOption(options_menu))
114
121
  options_menu.addAction(SyncOnStartOption(options_menu))
115
122
  options_menu.addMenu(AutoDownloadResourcesMenu(options_menu))
@@ -147,7 +154,6 @@ class QCanvasWindow(QMainWindow):
147
154
 
148
155
  @asyncSlot()
149
156
  async def _on_app_loaded(self) -> None:
150
- await self._qcanvas.init()
151
157
  self._course_tree.reload(await self._get_terms(), sync_receipt=empty_receipt())
152
158
 
153
159
  if settings.client.sync_on_start:
@@ -199,14 +205,14 @@ class QCanvasWindow(QMainWindow):
199
205
  await self._get_courses(), sync_receipt=receipt
200
206
  )
201
207
 
202
- async def _get_resources(self) -> Dict[str, db.Resource]:
203
- return (await self._qcanvas.get_data()).resources
208
+ async def _get_resources(self) -> dict[str, db.Resource]:
209
+ return (await self._qcanvas.load()).resources
204
210
 
205
211
  async def _get_terms(self) -> Sequence[db.Term]:
206
- return (await self._qcanvas.get_data()).terms
212
+ return (await self._qcanvas.load()).terms
207
213
 
208
214
  async def _get_courses(self) -> Sequence[db.Course]:
209
- return (await self._qcanvas.get_data()).courses
215
+ return (await self._qcanvas.load()).courses
210
216
 
211
217
  @Slot(db.Course)
212
218
  def _on_course_selected(self, course: Optional[db.Course]) -> None:
@@ -218,10 +224,9 @@ class QCanvasWindow(QMainWindow):
218
224
  @asyncSlot(db.Course, str)
219
225
  async def _on_course_renamed(self, course: db.Course, new_name: str) -> None:
220
226
  _logger.debug("Rename %s -> %s", course.name, new_name)
221
-
222
- async with self._qcanvas.database.session() as session:
223
- session.add(course)
224
- course.configuration.nickname = new_name
227
+ config = course_configs[course.id]
228
+ config.nickname = new_name
229
+ await config.save()
225
230
 
226
231
  @asyncSlot()
227
232
  async def _open_quick_auth_in_browser(self) -> None:
@@ -1,11 +1,10 @@
1
1
  import logging
2
2
  from asyncio import Lock
3
3
  from dataclasses import dataclass
4
- from typing import *
5
4
 
5
+ from libqcanvas.task_master import TaskID
6
+ from PySide6.QtWidgets import QProgressBar, QStatusBar
6
7
  from qasync import asyncSlot
7
- from qcanvas_backend.task_master import TaskID
8
- from qtpy.QtWidgets import *
9
8
 
10
9
  from qcanvas.backend_connectors import task_master
11
10
 
@@ -90,7 +89,7 @@ class StatusBarProgressDisplay(QStatusBar):
90
89
  self.showMessage("Done", 5000)
91
90
  self._progress_bar.hide()
92
91
 
93
- def _show_single_task_progress(self, task: Tuple[TaskID, _TaskProgress]) -> None:
92
+ def _show_single_task_progress(self, task: tuple[TaskID, _TaskProgress]) -> None:
94
93
  _logger.debug("Single task %s", task)
95
94
  id, progress = task
96
95
 
@@ -98,7 +97,7 @@ class StatusBarProgressDisplay(QStatusBar):
98
97
  self.showMessage(id.step_name)
99
98
 
100
99
  def _show_multiple_tasks_progress(
101
- self, tasks: list[Tuple[TaskID, _TaskProgress]]
100
+ self, tasks: list[tuple[TaskID, _TaskProgress]]
102
101
  ) -> None:
103
102
  _logger.debug("Multiple tasks %s", tasks)
104
103
  self.showMessage(
@@ -107,7 +106,7 @@ class StatusBarProgressDisplay(QStatusBar):
107
106
  self._show_progress(self._calculate_progress(tasks))
108
107
 
109
108
  def _calculate_progress(
110
- self, tasks: list[Tuple[TaskID, _TaskProgress]]
109
+ self, tasks: list[tuple[TaskID, _TaskProgress]]
111
110
  ) -> _TaskProgress:
112
111
  # Task progresses are floats from 0 to 1, multiplier is used to turn them into ints
113
112
  multiplier = 1000
@@ -0,0 +1,15 @@
1
+
2
+
3
+ /*
4
+ This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only.
5
+ It is supposed to be strictly declarative and only uses a subset of QML. If you edit
6
+ this file manually, you might introduce QML code that is not supported by Qt Design Studio.
7
+ Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files.
8
+ */
9
+ import QtQuick
10
+
11
+ ListView {
12
+ id: view
13
+ model: AttachmentsListModel {}
14
+ delegate: AttachmentsListDelegate {}
15
+ }
@@ -0,0 +1,77 @@
1
+ import QtQuick
2
+ import QtQuick.Layouts
3
+
4
+ Item {
5
+ id: delegate
6
+ height: childrenRect.height
7
+ anchors {
8
+ left: parent.left
9
+ right: parent.right
10
+ }
11
+
12
+ ColumnLayout {
13
+ spacing: 0
14
+
15
+ anchors {
16
+ left: parent.left
17
+ right: parent.right
18
+ }
19
+
20
+ RowLayout {
21
+ height: childrenRect.implicitHeight
22
+ Layout.fillWidth: true
23
+ spacing: 10
24
+
25
+ Image {
26
+ function attachmentIcon(downloadStatus) {
27
+ switch(downloadStatus)
28
+ {
29
+ case "DOWNLOADED":
30
+ return "qrc:///icons/universal/downloads/downloaded.svg";
31
+ case "FAILED":
32
+ return "qrc:///icons/universal/downloads/download_failed.svg";
33
+ case "NOT_DOWNLOADED":
34
+ return "qrc:///icons/universal/downloads/not_downloaded.svg";
35
+ default:
36
+ return "qrc:///icons/universal/downloads/unknown.svg"
37
+ }
38
+ }
39
+
40
+ id: fileIcon
41
+ source: attachmentIcon(modelData.download_state)
42
+ fillMode: Image.PreserveAspectFit
43
+ sourceSize.height: 17
44
+ sourceSize.width: 17
45
+ }
46
+
47
+ Text {
48
+ id: file_name_text
49
+ text: modelData.file_name
50
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
51
+ Layout.fillWidth: true
52
+ font.underline: true
53
+ color: palette.link
54
+
55
+ MouseArea {
56
+ id: textClickArea
57
+ anchors.fill: parent
58
+ cursorShape: Qt.PointingHandCursor
59
+ hoverEnabled: true
60
+ }
61
+ }
62
+ }
63
+
64
+ Spacer {
65
+ size: 5
66
+ visible: index !== count - 1
67
+ }
68
+ }
69
+
70
+ Connections {
71
+ target: textClickArea
72
+
73
+ function onClicked() {
74
+ modelData.opened(modelData.resource_id)
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,19 @@
1
+ import QtQuick
2
+
3
+ ListModel {
4
+ ListElement {
5
+ file_name: "texas.pdf"
6
+ resource_id: "1"
7
+ download_state: "NOT_DOWNLOADED"
8
+ }
9
+ ListElement {
10
+ file_name: "oh_no_what_a_terribly_long_file_name_its_not_like_someone_would_actually_do_this.pdf"
11
+ resource_id: "2"
12
+ download_state: "FAILED"
13
+ }
14
+ ListElement {
15
+ file_name: "i was transported to another world where javascript doesn't exist.cbz"
16
+ resource_id: "3"
17
+ download_state: "DOWNLOADED"
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ import QtQuick
2
+
3
+ ThemedRectangle {
4
+ anchors.fill: parent
5
+
6
+ AttachmentsList {
7
+ anchors.fill: parent
8
+ model: submission_files
9
+ palette: theme
10
+ }
11
+ }
@@ -0,0 +1,15 @@
1
+
2
+
3
+ /*
4
+ This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only.
5
+ It is supposed to be strictly declarative and only uses a subset of QML. If you edit
6
+ this file manually, you might introduce QML code that is not supported by Qt Design Studio.
7
+ Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files.
8
+ */
9
+ import QtQuick
10
+
11
+ ListView {
12
+ id: view
13
+ model: CommentsListModel {}
14
+ delegate: CommentsListDelegate {}
15
+ }