qcanvas 1.0.3.post5__tar.gz → 1.0.6a1__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.

Potentially problematic release.


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

Files changed (64) hide show
  1. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/PKG-INFO +2 -2
  2. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/pyproject.toml +2 -2
  3. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/backend_connectors/qcanvas_task_master.py +1 -1
  4. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/icons/file-download-failed.svg +5 -5
  5. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/icons/file-downloaded.svg +5 -5
  6. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/icons/file-not-downloaded.svg +5 -5
  7. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/icons/file-unknown.svg +5 -5
  8. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/run.py +1 -0
  9. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/content_tree.py +3 -3
  10. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/course_tree.py +12 -14
  11. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/assignment_tab/assignment_tree.py +1 -1
  12. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/content_tab.py +2 -0
  13. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/page_tab/page_tree.py +1 -1
  14. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/resource_rich_browser.py +5 -4
  15. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/main_ui/qcanvas_window.py +36 -17
  16. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/main_ui/status_bar_progress_display.py +3 -3
  17. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/memory_tree/memory_tree_widget.py +2 -2
  18. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/setup/setup_dialog.py +7 -4
  19. qcanvas-1.0.6a1/qcanvas/util/qurl_util.py +10 -0
  20. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/README.md +0 -0
  21. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/__init__.py +0 -0
  22. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/app_start/__init__.py +0 -0
  23. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/backend_connectors/__init__.py +0 -0
  24. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/backend_connectors/frontend_resource_manager.py +0 -0
  25. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/icons/__init__.py +0 -0
  26. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/icons/icons.qrc +0 -0
  27. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/icons/main_icon.svg +0 -0
  28. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/icons/rc_icons.py +0 -0
  29. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/icons/sync.svg +0 -0
  30. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/__init__.py +0 -0
  31. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/__init__.py +0 -0
  32. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/course_viewer.py +0 -0
  33. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/__init__.py +0 -0
  34. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/assignment_tab/__init__.py +0 -0
  35. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/assignment_tab/assignment_tab.py +0 -0
  36. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/mail_tab/__init__.py +0 -0
  37. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/mail_tab/mail_tab.py +0 -0
  38. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/mail_tab/mail_tree.py +0 -0
  39. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/page_tab/__init__.py +0 -0
  40. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/page_tab/page_tab.py +0 -0
  41. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/course_viewer/tabs/util.py +0 -0
  42. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/main_ui/__init__.py +0 -0
  43. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/main_ui/course_viewer_container.py +0 -0
  44. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/main_ui/options/__init__.py +0 -0
  45. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/main_ui/options/quick_sync_option.py +0 -0
  46. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/main_ui/options/sync_on_start_option.py +0 -0
  47. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/memory_tree/__init__.py +0 -0
  48. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/memory_tree/_tree_memory.py +0 -0
  49. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/memory_tree/memory_tree_widget_item.py +0 -0
  50. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/setup/__init__.py +0 -0
  51. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/ui/setup/setup_checker.py +0 -0
  52. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/__init__.py +0 -0
  53. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/basic_fonts.py +0 -0
  54. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/fe_resource_manager.py +0 -0
  55. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/html_cleaner.py +0 -0
  56. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/layouts.py +0 -0
  57. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/logs.py +0 -0
  58. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/paths.py +0 -0
  59. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/settings/__init__.py +0 -0
  60. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/settings/_client_settings.py +0 -0
  61. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/settings/_mapped_setting.py +0 -0
  62. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/settings/_ui_settings.py +0 -0
  63. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/ui_tools.py +0 -0
  64. {qcanvas-1.0.3.post5 → qcanvas-1.0.6a1}/qcanvas/util/url_checker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qcanvas
3
- Version: 1.0.3.post5
3
+ Version: 1.0.6a1
4
4
  Summary: QCanvas is a desktop client for Canvas LMS.
5
5
  Author: QCanvas
6
6
  Author-email: QCanvas@noreply.codeberg.org
@@ -15,7 +15,7 @@ Requires-Dist: platformdirs (>=4.2.2,<5.0.0)
15
15
  Requires-Dist: pyqtdarktheme-fork (>=2.3.2,<3.0.0)
16
16
  Requires-Dist: qasync (>=0.27.1,<0.28.0)
17
17
  Requires-Dist: qcanvas-api-clients (>=0.2.2,<0.3.0)
18
- Requires-Dist: qcanvas-backend (==0.1.10a1)
18
+ Requires-Dist: qcanvas-backend (==0.1.10a2)
19
19
  Requires-Dist: qtpy (>=2.4.1,<3.0.0)
20
20
  Requires-Dist: sqlalchemy (>=2.0.31,<3.0.0)
21
21
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "qcanvas"
3
- version = "1.0.3.post5"
3
+ version = "1.0.6a1"
4
4
  description = "QCanvas is a desktop client for Canvas LMS."
5
5
  authors = ["QCanvas <QCanvas@noreply.codeberg.org>"]
6
6
  readme = "README.md"
@@ -11,7 +11,7 @@ qcanvas = "qcanvas.run:main"
11
11
  [tool.poetry.dependencies]
12
12
  python = ">=3.11,<3.13"
13
13
  qcanvas-api-clients = "^0.2.2"
14
- qcanvas-backend = "0.1.10a1"
14
+ qcanvas-backend = "0.1.10a2"
15
15
  asynctaskpool = "^0.2.1"
16
16
  sqlalchemy = "^2.0.31"
17
17
  qtpy = "^2.4.1"
@@ -14,7 +14,7 @@ class _TaskMaster(TaskMaster, QObject, metaclass=_Meta):
14
14
  task_progress = Signal(TaskID, int, int)
15
15
  task_failed = Signal(TaskID, object)
16
16
 
17
- def report_failed(self, _task_id: TaskID, context: str | Exception) -> None:
17
+ def report_failed(self, _task_id: TaskID, context: object) -> None:
18
18
  self.task_failed.emit(_task_id, context)
19
19
 
20
20
  def report_progress(self, _task_id: TaskID, current: int, total: int) -> None:
@@ -1,6 +1,6 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
- <svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
4
- <title>file-times</title>
5
- <path d="M26.731 9.902c-0.005-0.035-0.011-0.066-0.019-0.095l0.001 0.005c-0.031-0.134-0.094-0.249-0.182-0.342l0 0-8-8c-0.092-0.087-0.207-0.15-0.335-0.181l-0.005-0.001c-0.027-0.008-0.059-0.014-0.092-0.019l-0.003-0c-0.026-0.007-0.059-0.014-0.092-0.019l-0.004-0h-12c-0.414 0-0.75 0.336-0.75 0.75v0 28c0 0.414 0.336 0.75 0.75 0.75h20c0.414-0 0.75-0.336 0.75-0.75v0-20c-0.005-0.038-0.012-0.071-0.020-0.103l0.001 0.005zM24.189 9.25h-5.439v-5.439zM6.75 29.25v-26.5h10.5v7.25c0 0.414 0.336 0.75 0.75 0.75h7.25v18.5zM20.066 15.935c-0.136-0.136-0.324-0.22-0.531-0.22s-0.395 0.084-0.531 0.22v0l-3.004 3.004-3.005-3.004c-0.136-0.141-0.327-0.228-0.538-0.228-0.414 0-0.75 0.336-0.75 0.75 0 0.211 0.087 0.402 0.228 0.538l3.004 3.005-3.004 3.004c-0.136 0.136-0.22 0.324-0.22 0.531 0 0.415 0.336 0.751 0.751 0.751 0.207 0 0.395-0.084 0.53-0.219v0l3.005-3.005 3.004 3.005c0.136 0.136 0.324 0.22 0.531 0.22 0.415 0 0.751-0.336 0.751-0.751 0-0.207-0.084-0.395-0.22-0.531l-3.005-3.004 3.005-3.005c0.135-0.136 0.218-0.323 0.218-0.53s-0.083-0.394-0.218-0.53l0 0z"></path>
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
4
+ <title>file-times</title>
5
+ <path d="M26.731 9.902c-0.005-0.035-0.011-0.066-0.019-0.095l0.001 0.005c-0.031-0.134-0.094-0.249-0.182-0.342l0 0-8-8c-0.092-0.087-0.207-0.15-0.335-0.181l-0.005-0.001c-0.027-0.008-0.059-0.014-0.092-0.019l-0.003-0c-0.026-0.007-0.059-0.014-0.092-0.019l-0.004-0h-12c-0.414 0-0.75 0.336-0.75 0.75v0 28c0 0.414 0.336 0.75 0.75 0.75h20c0.414-0 0.75-0.336 0.75-0.75v0-20c-0.005-0.038-0.012-0.071-0.020-0.103l0.001 0.005zM24.189 9.25h-5.439v-5.439zM6.75 29.25v-26.5h10.5v7.25c0 0.414 0.336 0.75 0.75 0.75h7.25v18.5zM20.066 15.935c-0.136-0.136-0.324-0.22-0.531-0.22s-0.395 0.084-0.531 0.22v0l-3.004 3.004-3.005-3.004c-0.136-0.141-0.327-0.228-0.538-0.228-0.414 0-0.75 0.336-0.75 0.75 0 0.211 0.087 0.402 0.228 0.538l3.004 3.005-3.004 3.004c-0.136 0.136-0.22 0.324-0.22 0.531 0 0.415 0.336 0.751 0.751 0.751 0.207 0 0.395-0.084 0.53-0.219v0l3.005-3.005 3.004 3.005c0.136 0.136 0.324 0.22 0.531 0.22 0.415 0 0.751-0.336 0.751-0.751 0-0.207-0.084-0.395-0.22-0.531l-3.005-3.004 3.005-3.005c0.135-0.136 0.218-0.323 0.218-0.53s-0.083-0.394-0.218-0.53l0 0z"></path>
6
6
  </svg>
@@ -1,6 +1,6 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
- <svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
4
- <title>file</title>
5
- <path d="M26.731 9.902c-0.005-0.035-0.011-0.066-0.019-0.095l0.001 0.005c-0.031-0.134-0.094-0.249-0.182-0.342l0 0-8-8c-0.092-0.087-0.207-0.15-0.335-0.181l-0.005-0.001c-0.027-0.008-0.059-0.014-0.092-0.019l-0.003-0c-0.026-0.007-0.059-0.014-0.092-0.019l-0.004-0h-12c-0.414 0-0.75 0.336-0.75 0.75v0 28c0 0.414 0.336 0.75 0.75 0.75h20c0.414-0 0.75-0.336 0.75-0.75v0-20c-0.005-0.038-0.012-0.071-0.020-0.103l0.001 0.005zM24.189 9.25h-5.439v-5.439zM6.75 29.25v-26.5h10.5v7.25c0 0.414 0.336 0.75 0.75 0.75h7.25v18.5z"></path>
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
4
+ <title>file</title>
5
+ <path d="M26.731 9.902c-0.005-0.035-0.011-0.066-0.019-0.095l0.001 0.005c-0.031-0.134-0.094-0.249-0.182-0.342l0 0-8-8c-0.092-0.087-0.207-0.15-0.335-0.181l-0.005-0.001c-0.027-0.008-0.059-0.014-0.092-0.019l-0.003-0c-0.026-0.007-0.059-0.014-0.092-0.019l-0.004-0h-12c-0.414 0-0.75 0.336-0.75 0.75v0 28c0 0.414 0.336 0.75 0.75 0.75h20c0.414-0 0.75-0.336 0.75-0.75v0-20c-0.005-0.038-0.012-0.071-0.020-0.103l0.001 0.005zM24.189 9.25h-5.439v-5.439zM6.75 29.25v-26.5h10.5v7.25c0 0.414 0.336 0.75 0.75 0.75h7.25v18.5z"></path>
6
6
  </svg>
@@ -1,6 +1,6 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
- <svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
4
- <title>file-arrow-down</title>
5
- <path d="M26.731 9.902c-0.005-0.035-0.011-0.066-0.019-0.095l0.001 0.005c-0.031-0.134-0.094-0.249-0.182-0.342l0 0-8-8c-0.092-0.087-0.207-0.15-0.335-0.181l-0.005-0.001c-0.027-0.008-0.059-0.014-0.092-0.019l-0.003-0c-0.026-0.007-0.059-0.014-0.092-0.019l-0.004-0h-12c-0.414 0-0.75 0.336-0.75 0.75v0 28c0 0.414 0.336 0.75 0.75 0.75h20c0.414-0 0.75-0.336 0.75-0.75v0-20c-0.005-0.038-0.012-0.071-0.020-0.103l0.001 0.005zM24.189 9.25h-5.439v-5.439zM6.75 29.25v-26.5h10.5v7.25c0 0.414 0.336 0.75 0.75 0.75h7.25v18.5zM20.469 20.91l-3.719 3.721v-10.676c0-0.414-0.336-0.75-0.75-0.75s-0.75 0.336-0.75 0.75v0 10.675l-3.72-3.72c-0.136-0.134-0.322-0.218-0.528-0.218-0.415 0-0.751 0.336-0.751 0.751 0 0.207 0.083 0.394 0.219 0.529l5 5c0.026 0.026 0.065 0.017 0.093 0.038 0.052 0.040 0.088 0.098 0.15 0.124 0.085 0.035 0.184 0.056 0.287 0.057h0c0 0 0 0 0 0 0.207 0 0.394-0.084 0.53-0.219l5.001-5c0.136-0.136 0.22-0.324 0.22-0.531 0-0.415-0.336-0.751-0.751-0.751-0.207 0-0.395 0.084-0.531 0.22v0z"></path>
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
4
+ <title>file-arrow-down</title>
5
+ <path d="M26.731 9.902c-0.005-0.035-0.011-0.066-0.019-0.095l0.001 0.005c-0.031-0.134-0.094-0.249-0.182-0.342l0 0-8-8c-0.092-0.087-0.207-0.15-0.335-0.181l-0.005-0.001c-0.027-0.008-0.059-0.014-0.092-0.019l-0.003-0c-0.026-0.007-0.059-0.014-0.092-0.019l-0.004-0h-12c-0.414 0-0.75 0.336-0.75 0.75v0 28c0 0.414 0.336 0.75 0.75 0.75h20c0.414-0 0.75-0.336 0.75-0.75v0-20c-0.005-0.038-0.012-0.071-0.020-0.103l0.001 0.005zM24.189 9.25h-5.439v-5.439zM6.75 29.25v-26.5h10.5v7.25c0 0.414 0.336 0.75 0.75 0.75h7.25v18.5zM20.469 20.91l-3.719 3.721v-10.676c0-0.414-0.336-0.75-0.75-0.75s-0.75 0.336-0.75 0.75v0 10.675l-3.72-3.72c-0.136-0.134-0.322-0.218-0.528-0.218-0.415 0-0.751 0.336-0.751 0.751 0 0.207 0.083 0.394 0.219 0.529l5 5c0.026 0.026 0.065 0.017 0.093 0.038 0.052 0.040 0.088 0.098 0.15 0.124 0.085 0.035 0.184 0.056 0.287 0.057h0c0 0 0 0 0 0 0.207 0 0.394-0.084 0.53-0.219l5.001-5c0.136-0.136 0.22-0.324 0.22-0.531 0-0.415-0.336-0.751-0.751-0.751-0.207 0-0.395 0.084-0.531 0.22v0z"></path>
6
6
  </svg>
@@ -1,6 +1,6 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
- <svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
4
- <title>question</title>
5
- <path d="M19.943 5.251c-1.145-0.635-2.511-1.008-3.964-1.008-1.461 0-2.834 0.378-4.026 1.040l0.042-0.022c-1.649 0.916-2.746 2.648-2.746 4.635 0 0.037 0 0.074 0.001 0.111l-0-0.006c0.001 0.413 0.336 0.747 0.75 0.747 0 0 0 0 0 0h-0c0.407-0.021 0.731-0.346 0.75-0.751l0-0.002c-0.001-0.026-0.001-0.058-0.001-0.089 0-1.441 0.8-2.694 1.98-3.341l0.020-0.010c0.934-0.511 2.047-0.812 3.23-0.812 1.178 0 2.287 0.298 3.255 0.824l-0.036-0.018c1.23 0.657 2.053 1.933 2.053 3.401 0 0.017-0 0.033-0 0.050l0-0.003c0.033 0.19 0.051 0.408 0.051 0.631 0 1.699-1.084 3.145-2.598 3.684l-0.027 0.008c-2.303 1.102-3.424 3.285-3.426 6.677 0 0 0 0 0 0.001 0 0.414 0.335 0.749 0.749 0.749h0.001c0.414-0 0.75-0.336 0.75-0.75v0c0.002-2.793 0.819-4.485 2.586-5.329 2.037-0.783 3.457-2.725 3.457-4.997 0-0.238-0.016-0.473-0.046-0.703l0.003 0.027c0-0.017 0-0.037 0-0.057 0-2.018-1.123-3.774-2.779-4.676l-0.028-0.014zM15.46 25.459c-0.123 0.145-0.198 0.334-0.2 0.541v0c-0.001 0.016-0.002 0.034-0.002 0.052 0 0.082 0.015 0.16 0.044 0.231l-0.002-0.004c0.039 0.094 0.093 0.173 0.16 0.24l0 0c0.071 0.076 0.158 0.137 0.255 0.178l0.005 0.002c0.084 0.026 0.18 0.041 0.28 0.041s0.196-0.015 0.287-0.043l-0.007 0.002c0.102-0.043 0.189-0.104 0.26-0.179l0-0c0.067-0.067 0.12-0.147 0.157-0.235l0.002-0.005c0.038-0.079 0.061-0.173 0.061-0.271 0-0.003-0-0.006-0-0.009v0c-0.010-0.209-0.092-0.397-0.221-0.542l0.001 0.001c-0.145-0.121-0.334-0.195-0.54-0.195s-0.395 0.074-0.541 0.196l0.001-0.001z"></path>
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
4
+ <title>question</title>
5
+ <path d="M19.943 5.251c-1.145-0.635-2.511-1.008-3.964-1.008-1.461 0-2.834 0.378-4.026 1.040l0.042-0.022c-1.649 0.916-2.746 2.648-2.746 4.635 0 0.037 0 0.074 0.001 0.111l-0-0.006c0.001 0.413 0.336 0.747 0.75 0.747 0 0 0 0 0 0h-0c0.407-0.021 0.731-0.346 0.75-0.751l0-0.002c-0.001-0.026-0.001-0.058-0.001-0.089 0-1.441 0.8-2.694 1.98-3.341l0.020-0.010c0.934-0.511 2.047-0.812 3.23-0.812 1.178 0 2.287 0.298 3.255 0.824l-0.036-0.018c1.23 0.657 2.053 1.933 2.053 3.401 0 0.017-0 0.033-0 0.050l0-0.003c0.033 0.19 0.051 0.408 0.051 0.631 0 1.699-1.084 3.145-2.598 3.684l-0.027 0.008c-2.303 1.102-3.424 3.285-3.426 6.677 0 0 0 0 0 0.001 0 0.414 0.335 0.749 0.749 0.749h0.001c0.414-0 0.75-0.336 0.75-0.75v0c0.002-2.793 0.819-4.485 2.586-5.329 2.037-0.783 3.457-2.725 3.457-4.997 0-0.238-0.016-0.473-0.046-0.703l0.003 0.027c0-0.017 0-0.037 0-0.057 0-2.018-1.123-3.774-2.779-4.676l-0.028-0.014zM15.46 25.459c-0.123 0.145-0.198 0.334-0.2 0.541v0c-0.001 0.016-0.002 0.034-0.002 0.052 0 0.082 0.015 0.16 0.044 0.231l-0.002-0.004c0.039 0.094 0.093 0.173 0.16 0.24l0 0c0.071 0.076 0.158 0.137 0.255 0.178l0.005 0.002c0.084 0.026 0.18 0.041 0.28 0.041s0.196-0.015 0.287-0.043l-0.007 0.002c0.102-0.043 0.189-0.104 0.26-0.179l0-0c0.067-0.067 0.12-0.147 0.157-0.235l0.002-0.005c0.038-0.079 0.061-0.173 0.061-0.271 0-0.003-0-0.006-0-0.009v0c-0.010-0.209-0.092-0.397-0.221-0.542l0.001 0.001c-0.145-0.121-0.334-0.195-0.54-0.195s-0.395 0.074-0.541 0.196l0.001-0.001z"></path>
6
6
  </svg>
@@ -7,6 +7,7 @@ from qcanvas.util import logs, paths
7
7
  paths.data_storage().mkdir(parents=True, exist_ok=True)
8
8
 
9
9
  logging.basicConfig(
10
+ filemode="w",
10
11
  filename=paths.data_storage() / "debug.log",
11
12
  level="WARN",
12
13
  )
@@ -3,7 +3,7 @@ from abc import abstractmethod
3
3
  from typing import *
4
4
 
5
5
  from qcanvas_backend.net.sync.sync_receipt import SyncReceipt
6
- from qtpy.QtCore import Signal, Slot
6
+ from qtpy.QtCore import QItemSelection, Signal, Slot
7
7
  from qtpy.QtWidgets import *
8
8
 
9
9
  from qcanvas.ui.memory_tree import MemoryTreeWidget, MemoryTreeWidgetItem
@@ -69,8 +69,8 @@ class ContentTree(MemoryTreeWidget, Generic[T]):
69
69
  self, data: T, sync_receipt: Optional[SyncReceipt]
70
70
  ) -> Sequence[MemoryTreeWidgetItem]: ...
71
71
 
72
- @Slot()
73
- def _selection_changed(self) -> None:
72
+ @Slot(QItemSelection, QItemSelection)
73
+ def _selection_changed(self, _0: QItemSelection, _1: QItemSelection) -> None:
74
74
  if self._reloading:
75
75
  return
76
76
 
@@ -3,7 +3,7 @@ from typing import *
3
3
 
4
4
  import qcanvas_backend.database.types as db
5
5
  from qcanvas_backend.net.sync.sync_receipt import SyncReceipt
6
- from qtpy.QtCore import QObject, Qt, Signal, Slot
6
+ from qtpy.QtCore import Qt, Signal
7
7
 
8
8
  from qcanvas.ui.course_viewer.content_tree import ContentTree
9
9
  from qcanvas.ui.memory_tree import MemoryTreeWidgetItem
@@ -11,18 +11,16 @@ from qcanvas.ui.memory_tree import MemoryTreeWidgetItem
11
11
  _logger = logging.getLogger(__name__)
12
12
 
13
13
 
14
- class _CourseTreeItem(MemoryTreeWidgetItem, QObject):
15
- renamed = Signal(db.Course, str)
16
-
17
- def __init__(self, course: db.Course):
14
+ class _CourseTreeItem(MemoryTreeWidgetItem):
15
+ def __init__(self, course: db.Course, owner: "CourseTree"):
18
16
  MemoryTreeWidgetItem.__init__(
19
17
  self,
20
18
  id=course.id,
21
19
  data=course,
22
20
  strings=[course.configuration.nickname or course.name],
23
21
  )
24
- QObject.__init__(self)
25
22
 
23
+ self._owner = owner
26
24
  self._course = course
27
25
 
28
26
  self.setFlags(
@@ -39,10 +37,10 @@ class _CourseTreeItem(MemoryTreeWidgetItem, QObject):
39
37
 
40
38
  if len(value) == 0:
41
39
  super().setData(column, role, self._course.name)
42
- self.renamed.emit(self._course, None)
40
+ self._owner.course_renamed.emit(self._course, None)
43
41
  else:
44
42
  super().setData(column, role, value)
45
- self.renamed.emit(self._course, value)
43
+ self._owner.course_renamed.emit(self._course, value)
46
44
 
47
45
 
48
46
  class CourseTree(ContentTree[Sequence[db.Term]]):
@@ -63,6 +61,7 @@ class CourseTree(ContentTree[Sequence[db.Term]]):
63
61
 
64
62
  for course in term.courses:
65
63
  course_widget = self._create_course_widget(course, sync_receipt)
64
+ # course_widget.renamed.connect(self._on_course_renamed)
66
65
  term_widget.addChild(course_widget)
67
66
 
68
67
  widgets.append(term_widget)
@@ -71,9 +70,8 @@ class CourseTree(ContentTree[Sequence[db.Term]]):
71
70
 
72
71
  def _create_course_widget(
73
72
  self, course: db.Course, sync_receipt: Optional[SyncReceipt]
74
- ) -> MemoryTreeWidgetItem:
75
- course_widget = _CourseTreeItem(course)
76
- course_widget.renamed.connect(self._on_course_renamed)
73
+ ) -> _CourseTreeItem:
74
+ course_widget = _CourseTreeItem(course, self)
77
75
 
78
76
  is_new = sync_receipt is not None and course.id in sync_receipt.updated_courses
79
77
 
@@ -88,6 +86,6 @@ class CourseTree(ContentTree[Sequence[db.Term]]):
88
86
 
89
87
  return term_widget
90
88
 
91
- @Slot()
92
- def _on_course_renamed(self, course: db.Course, new_name: str) -> None:
93
- self.course_renamed.emit(course, new_name)
89
+ # @Slot(db.Course, str)
90
+ # def _on_course_renamed(self, course: db.Course, new_name: str) -> None:
91
+ # self.course_renamed.emit(course, new_name)
@@ -3,7 +3,7 @@ from typing import Optional, Sequence
3
3
 
4
4
  import qcanvas_backend.database.types as db
5
5
  from qcanvas_backend.net.sync.sync_receipt import SyncReceipt
6
- from qtpy.QtGui import Qt
6
+ from qtpy.QtCore import Qt
7
7
  from qtpy.QtWidgets import QHeaderView
8
8
 
9
9
  from qcanvas.ui.course_viewer.content_tree import ContentTree
@@ -4,6 +4,7 @@ from typing import *
4
4
  import qcanvas_backend.database.types as db
5
5
  from qcanvas_backend.net.resources.download.resource_manager import ResourceManager
6
6
  from qcanvas_backend.net.sync.sync_receipt import SyncReceipt
7
+ from qtpy.QtCore import Slot
7
8
  from qtpy.QtWidgets import *
8
9
 
9
10
  from qcanvas.ui.course_viewer.content_tree import ContentTree
@@ -70,6 +71,7 @@ class ContentTab(QWidget):
70
71
  def reload(self, course: db.Course, *, sync_receipt: Optional[SyncReceipt]) -> None:
71
72
  self._explorer.reload(course, sync_receipt=sync_receipt)
72
73
 
74
+ @Slot(object)
73
75
  def _item_selected(self, item: object) -> None:
74
76
  if isinstance(item, db.CourseContentItem):
75
77
  _logger.debug("Show %s", item.name)
@@ -3,7 +3,7 @@ from typing import Optional, Sequence
3
3
 
4
4
  import qcanvas_backend.database.types as db
5
5
  from qcanvas_backend.net.sync.sync_receipt import SyncReceipt
6
- from qtpy.QtGui import Qt
6
+ from qtpy.QtCore import Qt
7
7
 
8
8
  from qcanvas.ui.course_viewer.content_tree import ContentTree
9
9
  from qcanvas.ui.memory_tree import MemoryTreeWidgetItem
@@ -14,6 +14,7 @@ from qtpy.QtWidgets import QTextBrowser
14
14
  from qcanvas import icons
15
15
  from qcanvas.backend_connectors import FrontendResourceManager
16
16
  from qcanvas.util.html_cleaner import clean_up_html
17
+ from qcanvas.util.qurl_util import file_url
17
18
 
18
19
  _logger = logging.getLogger(__name__)
19
20
 
@@ -148,7 +149,7 @@ class ResourceRichBrowser(QTextBrowser):
148
149
  case _:
149
150
  raise ValueError()
150
151
 
151
- @asyncSlot()
152
+ @asyncSlot(QUrl)
152
153
  async def _open_url(self, url: QUrl) -> None:
153
154
  if url.scheme() == "data":
154
155
  await self._open_resource_from_link(url)
@@ -167,10 +168,10 @@ class ResourceRichBrowser(QTextBrowser):
167
168
  )
168
169
  return
169
170
 
170
- resource_path = self._downloader.resource_download_location(resource)
171
- QDesktopServices.openUrl(QUrl.fromLocalFile(resource_path.absolute()))
171
+ resource_path = file_url(self._downloader.resource_download_location(resource))
172
+ QDesktopServices.openUrl(resource_path)
172
173
 
173
- @Slot()
174
+ @Slot(db.Resource)
174
175
  def _download_updated(self, resource: db.Resource) -> None:
175
176
  if self._content is not None and resource.id in self._current_content_resources:
176
177
  self._show_page_content(self._content)
@@ -5,9 +5,10 @@ from typing import *
5
5
  import qcanvas_backend.database.types as db
6
6
  from qasync import asyncSlot
7
7
  from qcanvas_backend.database.data_monolith import DataMonolith
8
+ from qcanvas_backend.net.sync.sync_receipt import SyncReceipt
8
9
  from qcanvas_backend.qcanvas import QCanvas
9
10
  from qtpy.QtCore import QUrl, Signal, Slot
10
- from qtpy.QtGui import QDesktopServices, QKeySequence, QPixmap
11
+ from qtpy.QtGui import QDesktopServices, QIcon, QKeySequence
11
12
  from qtpy.QtWidgets import *
12
13
 
13
14
  from qcanvas import icons
@@ -18,6 +19,7 @@ from qcanvas.ui.main_ui.options.quick_sync_option import QuickSyncOption
18
19
  from qcanvas.ui.main_ui.options.sync_on_start_option import SyncOnStartOption
19
20
  from qcanvas.ui.main_ui.status_bar_progress_display import StatusBarProgressDisplay
20
21
  from qcanvas.util import paths, settings
22
+ from qcanvas.util.qurl_util import file_url
21
23
  from qcanvas.util.ui_tools import create_qaction
22
24
 
23
25
  _logger = logging.getLogger(__name__)
@@ -30,7 +32,7 @@ class QCanvasWindow(QMainWindow):
30
32
  super().__init__()
31
33
 
32
34
  self.setWindowTitle("QCanvas")
33
- self.setWindowIcon(QPixmap(icons.main_icon))
35
+ self.setWindowIcon(QIcon(icons.main_icon))
34
36
 
35
37
  self._operation_semaphore = Semaphore()
36
38
  self._data: Optional[DataMonolith] = None
@@ -83,6 +85,10 @@ class QCanvasWindow(QMainWindow):
83
85
  parent=app_menu,
84
86
  )
85
87
 
88
+ create_qaction(
89
+ name="Mark all as seen", triggered=self._clear_new_items, parent=app_menu
90
+ )
91
+
86
92
  create_qaction(
87
93
  name="Quit",
88
94
  shortcut=QKeySequence("Ctrl+Q"),
@@ -96,8 +102,11 @@ class QCanvasWindow(QMainWindow):
96
102
  options_menu.addAction(SyncOnStartOption(options_menu))
97
103
 
98
104
  def _restore_window_position(self):
99
- self.restoreGeometry(settings.ui.last_geometry)
100
- self.restoreState(settings.ui.last_window_state)
105
+ if settings.ui.last_geometry is not None:
106
+ self.restoreGeometry(settings.ui.last_geometry)
107
+
108
+ if settings.ui.last_window_state is not None:
109
+ self.restoreState(settings.ui.last_window_state)
101
110
 
102
111
  def closeEvent(self, event):
103
112
  settings.ui.last_geometry = self.saveGeometry()
@@ -143,30 +152,36 @@ class QCanvasWindow(QMainWindow):
143
152
  receipt = await self._qcanvas.synchronise_canvas(
144
153
  quick_sync=settings.client.quick_sync_enabled
145
154
  )
146
-
147
- self._course_tree.reload(await self._get_terms(), sync_receipt=receipt)
148
- await self._course_viewer_container.reload_all(
149
- await self._get_courses(), sync_receipt=receipt
150
- )
155
+ await self._reload(receipt)
151
156
  self._sync_button.setText("Synchronise")
157
+ except Exception as e:
158
+ error = QErrorMessage(self)
159
+ error.showMessage(str(e))
152
160
 
161
+ raise e
153
162
  finally:
154
163
  self._operation_semaphore.release()
155
164
 
165
+ async def _reload(self, receipt: Optional[SyncReceipt]) -> None:
166
+ self._course_tree.reload(await self._get_terms(), sync_receipt=receipt)
167
+ await self._course_viewer_container.reload_all(
168
+ await self._get_courses(), sync_receipt=receipt
169
+ )
170
+
156
171
  async def _get_terms(self) -> Sequence[db.Term]:
157
172
  return (await self._qcanvas.get_data()).terms
158
173
 
159
174
  async def _get_courses(self) -> Sequence[db.Course]:
160
175
  return (await self._qcanvas.get_data()).courses
161
176
 
162
- @Slot()
177
+ @Slot(db.Course)
163
178
  def _on_course_selected(self, course: Optional[db.Course]) -> None:
164
179
  if course is not None:
165
180
  self._course_viewer_container.load_course(course)
166
181
  else:
167
182
  self._course_viewer_container.show_blank()
168
183
 
169
- @asyncSlot()
184
+ @asyncSlot(db.Course, str)
170
185
  async def _on_course_renamed(self, course: db.Course, new_name: str) -> None:
171
186
  _logger.debug("Rename %s -> %s", course.name, new_name)
172
187
 
@@ -175,18 +190,22 @@ class QCanvasWindow(QMainWindow):
175
190
  course.configuration.nickname = new_name
176
191
 
177
192
  @asyncSlot()
178
- async def _open_quick_auth_in_browser(self):
193
+ async def _open_quick_auth_in_browser(self) -> None:
179
194
  opening_progress_dialog = QProgressDialog("Opening canvas", None, 0, 0, self)
180
195
  opening_progress_dialog.setWindowTitle("Please wait")
181
196
  opening_progress_dialog.show()
182
197
  QDesktopServices.openUrl(
183
- await self._qcanvas.canvas_client.get_temporary_session_url()
198
+ QUrl(await self._qcanvas.canvas_client.get_temporary_session_url())
184
199
  )
185
200
  opening_progress_dialog.close()
186
201
 
187
202
  @Slot()
188
203
  def _open_downloads_folder(self) -> None:
189
- # fixme hard coded path! >:(
190
- QDesktopServices.openUrl(
191
- QUrl(f"file://{(paths.data_storage() / 'downloads').absolute()}")
192
- )
204
+ directory = self._qcanvas.resource_manager.downloads_folder
205
+ directory.mkdir(parents=True, exist_ok=True)
206
+
207
+ QDesktopServices.openUrl(file_url(directory))
208
+
209
+ @asyncSlot()
210
+ async def _clear_new_items(self) -> None:
211
+ await self._reload(None)
@@ -38,7 +38,7 @@ class StatusBarProgressDisplay(QStatusBar):
38
38
  bar.hide()
39
39
  return bar
40
40
 
41
- @asyncSlot()
41
+ @asyncSlot(TaskID, int, int)
42
42
  async def _on_task_progress(
43
43
  self, task_id: TaskID, current: int, total: int
44
44
  ) -> None:
@@ -60,8 +60,8 @@ class StatusBarProgressDisplay(QStatusBar):
60
60
  task.current = current
61
61
  task.total = total
62
62
 
63
- @asyncSlot()
64
- async def _on_task_failed(self, task_id: TaskID, context: str | Exception) -> None:
63
+ @asyncSlot(TaskID, object)
64
+ async def _on_task_failed(self, task_id: TaskID, context: object) -> None:
65
65
  _logger.debug("%s failed", task_id)
66
66
 
67
67
  async with self._lock:
@@ -114,7 +114,7 @@ class MemoryTreeWidget(QTreeWidget):
114
114
 
115
115
  self._id_map.update(map_updates.items())
116
116
 
117
- @Slot()
117
+ @Slot(QTreeWidgetItem)
118
118
  def _expanded(self, item: QTreeWidgetItem):
119
119
  if self._suppress_expansion_signals:
120
120
  return
@@ -123,7 +123,7 @@ class MemoryTreeWidget(QTreeWidget):
123
123
  _logger.debug("Expanded %s", item.id)
124
124
  self._memory.expanded(item.id)
125
125
 
126
- @Slot()
126
+ @Slot(QTreeWidgetItem)
127
127
  def _collapsed(self, item: QTreeWidgetItem):
128
128
  if self._suppress_expansion_signals:
129
129
  return
@@ -6,7 +6,7 @@ from qcanvas_api_clients.canvas import CanvasClient, CanvasClientConfig
6
6
  from qcanvas_api_clients.panopto import PanoptoClient, PanoptoClientConfig
7
7
  from qcanvas_api_clients.util.request_exceptions import ConfigInvalidError
8
8
  from qtpy.QtCore import QUrl, Signal, Slot
9
- from qtpy.QtGui import QDesktopServices, QPixmap
9
+ from qtpy.QtGui import QDesktopServices, QIcon
10
10
  from qtpy.QtWidgets import *
11
11
 
12
12
  import qcanvas.util.settings as settings
@@ -30,7 +30,7 @@ class SetupDialog(QDialog):
30
30
  self.setWindowTitle("Configure QCanvas")
31
31
  self.setMinimumSize(550, 200)
32
32
  self.resize(550, 200)
33
- self.setWindowIcon(QPixmap(icons.main_icon))
33
+ self.setWindowIcon(QIcon(icons.main_icon))
34
34
 
35
35
  self._semaphore = Semaphore()
36
36
  self._canvas_url_box = QLineEdit(settings.client.canvas_url)
@@ -76,7 +76,6 @@ class SetupDialog(QDialog):
76
76
  size_policy.setRetainSizeWhenHidden(True)
77
77
  widget.setSizePolicy(size_policy)
78
78
 
79
- # @Slot()
80
79
  @asyncSlot()
81
80
  async def _accepted(self) -> None:
82
81
  if self._semaphore.acquire(False):
@@ -208,5 +207,9 @@ class SetupDialog(QDialog):
208
207
  "Don't share this key. You can revoke it at any time.",
209
208
  parent=self,
210
209
  )
211
- msg.accepted.connect(lambda: QDesktopServices.openUrl(_tutorial_url))
210
+ msg.accepted.connect(self._open_tutorial)
212
211
  msg.show()
212
+
213
+ @Slot()
214
+ def _open_tutorial(self) -> None:
215
+ QDesktopServices.openUrl(QUrl(_tutorial_url))
@@ -0,0 +1,10 @@
1
+ import logging
2
+ from pathlib import Path
3
+
4
+ from qtpy.QtCore import QUrl
5
+
6
+ _logger = logging.getLogger(__name__)
7
+
8
+
9
+ def file_url(path: Path) -> QUrl:
10
+ return QUrl.fromLocalFile(str(path.absolute()))
File without changes