qcanvas 0.0.5.7a0__py3-none-any.whl → 1.0.3.post1__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.
- qcanvas/app_start/__init__.py +47 -0
- qcanvas/backend_connectors/__init__.py +2 -0
- qcanvas/backend_connectors/frontend_resource_manager.py +63 -0
- qcanvas/backend_connectors/qcanvas_task_master.py +28 -0
- qcanvas/icons/__init__.py +6 -0
- qcanvas/icons/file-download-failed.svg +6 -0
- qcanvas/icons/file-downloaded.svg +6 -0
- qcanvas/icons/file-not-downloaded.svg +6 -0
- qcanvas/icons/file-unknown.svg +6 -0
- qcanvas/icons/icons.qrc +4 -0
- qcanvas/icons/main_icon.svg +7 -7
- qcanvas/icons/rc_icons.py +580 -214
- qcanvas/icons/sync.svg +6 -6
- qcanvas/run.py +29 -0
- qcanvas/ui/course_viewer/__init__.py +2 -0
- qcanvas/ui/course_viewer/content_tree.py +123 -0
- qcanvas/ui/course_viewer/course_tree.py +93 -0
- qcanvas/ui/course_viewer/course_viewer.py +62 -0
- qcanvas/ui/course_viewer/tabs/__init__.py +3 -0
- qcanvas/ui/course_viewer/tabs/assignment_tab/__init__.py +1 -0
- qcanvas/ui/course_viewer/tabs/assignment_tab/assignment_tab.py +168 -0
- qcanvas/ui/course_viewer/tabs/assignment_tab/assignment_tree.py +104 -0
- qcanvas/ui/course_viewer/tabs/content_tab.py +96 -0
- qcanvas/ui/course_viewer/tabs/mail_tab/__init__.py +1 -0
- qcanvas/ui/course_viewer/tabs/mail_tab/mail_tab.py +68 -0
- qcanvas/ui/course_viewer/tabs/mail_tab/mail_tree.py +70 -0
- qcanvas/ui/course_viewer/tabs/page_tab/__init__.py +1 -0
- qcanvas/ui/course_viewer/tabs/page_tab/page_tab.py +36 -0
- qcanvas/ui/course_viewer/tabs/page_tab/page_tree.py +74 -0
- qcanvas/ui/course_viewer/tabs/resource_rich_browser.py +176 -0
- qcanvas/ui/course_viewer/tabs/util.py +1 -0
- qcanvas/ui/main_ui/course_viewer_container.py +52 -0
- qcanvas/ui/main_ui/options/__init__.py +3 -0
- qcanvas/ui/main_ui/options/quick_sync_option.py +25 -0
- qcanvas/ui/main_ui/options/sync_on_start_option.py +25 -0
- qcanvas/ui/main_ui/qcanvas_window.py +192 -0
- qcanvas/ui/main_ui/status_bar_progress_display.py +153 -0
- qcanvas/ui/memory_tree/__init__.py +2 -0
- qcanvas/ui/memory_tree/_tree_memory.py +66 -0
- qcanvas/ui/memory_tree/memory_tree_widget.py +133 -0
- qcanvas/ui/memory_tree/memory_tree_widget_item.py +19 -0
- qcanvas/ui/setup/__init__.py +2 -0
- qcanvas/ui/setup/setup_checker.py +17 -0
- qcanvas/ui/setup/setup_dialog.py +212 -0
- qcanvas/util/__init__.py +2 -0
- qcanvas/util/basic_fonts.py +12 -0
- qcanvas/util/fe_resource_manager.py +23 -0
- qcanvas/util/html_cleaner.py +25 -0
- qcanvas/util/layouts.py +52 -0
- qcanvas/util/logs.py +6 -0
- qcanvas/util/paths.py +41 -0
- qcanvas/util/settings/__init__.py +9 -0
- qcanvas/util/settings/_client_settings.py +29 -0
- qcanvas/util/settings/_mapped_setting.py +63 -0
- qcanvas/util/settings/_ui_settings.py +34 -0
- qcanvas/util/ui_tools.py +41 -0
- qcanvas/util/url_checker.py +13 -0
- qcanvas-1.0.3.post1.dist-info/METADATA +59 -0
- qcanvas-1.0.3.post1.dist-info/RECORD +64 -0
- {qcanvas-0.0.5.7a0.dist-info → qcanvas-1.0.3.post1.dist-info}/WHEEL +1 -1
- qcanvas-1.0.3.post1.dist-info/entry_points.txt +3 -0
- qcanvas/__main__.py +0 -155
- qcanvas/db/__init__.py +0 -5
- qcanvas/db/database.py +0 -338
- qcanvas/db/db_converter_helper.py +0 -81
- qcanvas/net/canvas/__init__.py +0 -2
- qcanvas/net/canvas/canvas_client.py +0 -209
- qcanvas/net/canvas/legacy_canvas_types.py +0 -124
- qcanvas/net/custom_httpx_async_transport.py +0 -34
- qcanvas/net/self_authenticating.py +0 -108
- qcanvas/queries/__init__.py +0 -4
- qcanvas/queries/all_courses.gql +0 -7
- qcanvas/queries/all_courses.py +0 -108
- qcanvas/queries/canvas_course_data.gql +0 -51
- qcanvas/queries/canvas_course_data.py +0 -143
- qcanvas/ui/container_item.py +0 -11
- qcanvas/ui/main_ui.py +0 -251
- qcanvas/ui/menu_bar/__init__.py +0 -0
- qcanvas/ui/menu_bar/grouping_preferences_menu.py +0 -61
- qcanvas/ui/menu_bar/theme_selection_menu.py +0 -39
- qcanvas/ui/setup_dialog.py +0 -190
- qcanvas/ui/status_bar_reporter.py +0 -40
- qcanvas/ui/viewer/__init__.py +0 -0
- qcanvas/ui/viewer/course_list.py +0 -96
- qcanvas/ui/viewer/file_list.py +0 -195
- qcanvas/ui/viewer/file_view_tab.py +0 -62
- qcanvas/ui/viewer/page_list_viewer.py +0 -150
- qcanvas/util/app_settings.py +0 -98
- qcanvas/util/constants.py +0 -5
- qcanvas/util/course_indexer/__init__.py +0 -1
- qcanvas/util/course_indexer/conversion_helpers.py +0 -78
- qcanvas/util/course_indexer/data_manager.py +0 -447
- qcanvas/util/course_indexer/resource_helpers.py +0 -191
- qcanvas/util/download_pool.py +0 -58
- qcanvas/util/helpers/__init__.py +0 -0
- qcanvas/util/helpers/canvas_sanitiser.py +0 -47
- qcanvas/util/helpers/file_icon_helper.py +0 -34
- qcanvas/util/helpers/qaction_helper.py +0 -25
- qcanvas/util/helpers/theme_helper.py +0 -48
- qcanvas/util/link_scanner/__init__.py +0 -2
- qcanvas/util/link_scanner/canvas_link_scanner.py +0 -41
- qcanvas/util/link_scanner/canvas_media_object_scanner.py +0 -60
- qcanvas/util/link_scanner/dropbox_scanner.py +0 -68
- qcanvas/util/link_scanner/resource_scanner.py +0 -69
- qcanvas/util/progress_reporter.py +0 -101
- qcanvas/util/self_updater.py +0 -55
- qcanvas/util/task_pool.py +0 -253
- qcanvas/util/tree_util/__init__.py +0 -3
- qcanvas/util/tree_util/expanding_tree.py +0 -165
- qcanvas/util/tree_util/model_helpers.py +0 -36
- qcanvas/util/tree_util/tree_model.py +0 -85
- qcanvas-0.0.5.7a0.dist-info/METADATA +0 -21
- qcanvas-0.0.5.7a0.dist-info/RECORD +0 -62
- /qcanvas/{net → ui/main_ui}/__init__.py +0 -0
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
|
|
3
|
-
"""
|
|
4
|
-
from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
|
|
5
|
-
from datetime import datetime # noqa: F401 # pylint: disable=W0611
|
|
6
|
-
from enum import Enum # noqa: F401 # pylint: disable=W0611
|
|
7
|
-
from typing import ( # noqa: F401 # pylint: disable=W0611
|
|
8
|
-
Any,
|
|
9
|
-
Optional,
|
|
10
|
-
Union,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
from pydantic import ( # noqa: F401 # pylint: disable=W0611
|
|
14
|
-
BaseModel,
|
|
15
|
-
Extra,
|
|
16
|
-
Field,
|
|
17
|
-
Json,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
DEFINITION = """
|
|
21
|
-
fragment CanvasCourseData on Course {
|
|
22
|
-
_id
|
|
23
|
-
name
|
|
24
|
-
courseNickname
|
|
25
|
-
term {
|
|
26
|
-
endAt
|
|
27
|
-
startAt
|
|
28
|
-
name
|
|
29
|
-
id
|
|
30
|
-
}
|
|
31
|
-
assignmentsConnection @include(if: $detailed) {
|
|
32
|
-
nodes {
|
|
33
|
-
description
|
|
34
|
-
courseId
|
|
35
|
-
dueAt
|
|
36
|
-
createdAt
|
|
37
|
-
id
|
|
38
|
-
name
|
|
39
|
-
position
|
|
40
|
-
updatedAt
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
modulesConnection @include(if: $detailed) {
|
|
44
|
-
nodes {
|
|
45
|
-
name
|
|
46
|
-
id
|
|
47
|
-
moduleItems {
|
|
48
|
-
content {
|
|
49
|
-
... on File {
|
|
50
|
-
_id
|
|
51
|
-
displayName
|
|
52
|
-
createdAt
|
|
53
|
-
updatedAt
|
|
54
|
-
url
|
|
55
|
-
size
|
|
56
|
-
mimeClass
|
|
57
|
-
contentType
|
|
58
|
-
}
|
|
59
|
-
... on Page {
|
|
60
|
-
_id
|
|
61
|
-
title
|
|
62
|
-
updatedAt
|
|
63
|
-
createdAt
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
"""
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
class ConfiguredBaseModel(BaseModel):
|
|
74
|
-
class Config:
|
|
75
|
-
smart_union = True
|
|
76
|
-
extra = Extra.forbid
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
class Term(ConfiguredBaseModel):
|
|
80
|
-
end_at: Optional[datetime] = Field(..., alias="endAt")
|
|
81
|
-
start_at: Optional[datetime] = Field(..., alias="startAt")
|
|
82
|
-
name: Optional[str] = Field(..., alias="name")
|
|
83
|
-
q_id: str = Field(..., alias="id")
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class Assignment(ConfiguredBaseModel):
|
|
87
|
-
description: Optional[str] = Field(..., alias="description")
|
|
88
|
-
course_id: Optional[str] = Field(..., alias="courseId")
|
|
89
|
-
due_at: Optional[datetime] = Field(..., alias="dueAt")
|
|
90
|
-
created_at: Optional[datetime] = Field(..., alias="createdAt")
|
|
91
|
-
q_id: str = Field(..., alias="id")
|
|
92
|
-
name: Optional[str] = Field(..., alias="name")
|
|
93
|
-
position: Optional[int] = Field(..., alias="position")
|
|
94
|
-
updated_at: Optional[datetime] = Field(..., alias="updatedAt")
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
class AssignmentConnection(ConfiguredBaseModel):
|
|
98
|
-
nodes: Optional[list[Optional[Assignment]]] = Field(..., alias="nodes")
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class ModuleItemInterface(ConfiguredBaseModel):
|
|
102
|
-
...
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
class File(ModuleItemInterface):
|
|
106
|
-
m_id: str = Field(..., alias="_id")
|
|
107
|
-
display_name: Optional[str] = Field(..., alias="displayName")
|
|
108
|
-
created_at: Optional[datetime] = Field(..., alias="createdAt")
|
|
109
|
-
updated_at: Optional[datetime] = Field(..., alias="updatedAt")
|
|
110
|
-
url: Optional[str] = Field(..., alias="url")
|
|
111
|
-
size: Optional[str] = Field(..., alias="size")
|
|
112
|
-
mime_class: Optional[str] = Field(..., alias="mimeClass")
|
|
113
|
-
content_type: Optional[str] = Field(..., alias="contentType")
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
class Page(ModuleItemInterface):
|
|
117
|
-
m_id: str = Field(..., alias="_id")
|
|
118
|
-
title: Optional[str] = Field(..., alias="title")
|
|
119
|
-
updated_at: Optional[datetime] = Field(..., alias="updatedAt")
|
|
120
|
-
created_at: Optional[datetime] = Field(..., alias="createdAt")
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
class ModuleItem(ConfiguredBaseModel):
|
|
124
|
-
content: Optional[Union[File, Page, ModuleItemInterface]] = Field(..., alias="content")
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
class Module(ConfiguredBaseModel):
|
|
128
|
-
name: Optional[str] = Field(..., alias="name")
|
|
129
|
-
q_id: str = Field(..., alias="id")
|
|
130
|
-
module_items: Optional[list[ModuleItem]] = Field(..., alias="moduleItems")
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
class ModuleConnection(ConfiguredBaseModel):
|
|
134
|
-
nodes: Optional[list[Optional[Module]]] = Field(..., alias="nodes")
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
class CanvasCourseData(ConfiguredBaseModel):
|
|
138
|
-
m_id: str = Field(..., alias="_id")
|
|
139
|
-
name: str = Field(..., alias="name")
|
|
140
|
-
course_nickname: Optional[str] = Field(..., alias="courseNickname")
|
|
141
|
-
term: Optional[Term] = Field(..., alias="term")
|
|
142
|
-
assignments_connection: Optional[AssignmentConnection] = Field(None, alias="assignmentsConnection")
|
|
143
|
-
modules_connection: Optional[ModuleConnection] = Field(None, alias="modulesConnection")
|
qcanvas/ui/container_item.py
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
from PySide6.QtGui import QStandardItem
|
|
2
|
-
|
|
3
|
-
from qcanvas.util import tree_util as tree
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class ContainerItem(QStandardItem):
|
|
7
|
-
def __init__(self, data: tree.HasText):
|
|
8
|
-
super().__init__()
|
|
9
|
-
self.content = data
|
|
10
|
-
self.setEditable(False)
|
|
11
|
-
self.setText(data.text)
|
qcanvas/ui/main_ui.py
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import sys
|
|
3
|
-
import traceback
|
|
4
|
-
from typing import Sequence, Optional
|
|
5
|
-
|
|
6
|
-
from PySide6.QtCore import Slot, Signal, Qt, QUrl, QObject
|
|
7
|
-
from PySide6.QtGui import QDesktopServices, QKeySequence
|
|
8
|
-
from PySide6.QtWidgets import *
|
|
9
|
-
from qasync import asyncSlot
|
|
10
|
-
|
|
11
|
-
import qcanvas.db.database as db
|
|
12
|
-
from qcanvas.ui.menu_bar.grouping_preferences_menu import GroupingPreferencesMenu
|
|
13
|
-
from qcanvas.ui.menu_bar.theme_selection_menu import ThemeSelectionMenu
|
|
14
|
-
from qcanvas.ui.status_bar_reporter import StatusBarReporter
|
|
15
|
-
from qcanvas.ui.viewer.course_list import CourseList
|
|
16
|
-
from qcanvas.ui.viewer.file_list import FileRow
|
|
17
|
-
from qcanvas.ui.viewer.file_view_tab import FileViewTab
|
|
18
|
-
from qcanvas.ui.viewer.page_list_viewer import AssignmentsViewer, PagesViewer, LinkTransformer
|
|
19
|
-
from qcanvas.util import self_updater
|
|
20
|
-
from qcanvas.util.app_settings import settings
|
|
21
|
-
from qcanvas.util.constants import app_name
|
|
22
|
-
from qcanvas.util.course_indexer import DataManager
|
|
23
|
-
from qcanvas.util.helpers.qaction_helper import create_qaction
|
|
24
|
-
|
|
25
|
-
_aux_settings = settings.auxiliary
|
|
26
|
-
_no_course_selected_text = "No course selected"
|
|
27
|
-
|
|
28
|
-
class AppMainWindow(QMainWindow):
|
|
29
|
-
logger = logging.getLogger()
|
|
30
|
-
loaded = Signal()
|
|
31
|
-
files_grouping_preference_changed = Signal(db.GroupByPreference)
|
|
32
|
-
|
|
33
|
-
def __init__(self, data_manager: DataManager, parent: QWidget | None = None):
|
|
34
|
-
super().__init__(parent)
|
|
35
|
-
|
|
36
|
-
self.selected_course: db.Course | None = None
|
|
37
|
-
self.courses: Sequence[db.Course] = []
|
|
38
|
-
self.resources: dict[str, db.Resource] = {}
|
|
39
|
-
self.data_manager = data_manager
|
|
40
|
-
self.link_transformer = LinkTransformer(self.data_manager.link_scanners, self.resources)
|
|
41
|
-
|
|
42
|
-
right_splitter = QSplitter()
|
|
43
|
-
right_splitter.setOrientation(Qt.Orientation.Vertical)
|
|
44
|
-
|
|
45
|
-
self.sync_button = QPushButton("Synchronize")
|
|
46
|
-
self.sync_button.clicked.connect(self.sync_data)
|
|
47
|
-
|
|
48
|
-
self.course_list = CourseList(self.data_manager)
|
|
49
|
-
self.course_list.course_selected.connect(self.on_course_selected)
|
|
50
|
-
|
|
51
|
-
self.assignment_viewer = AssignmentsViewer(self.link_transformer)
|
|
52
|
-
self.assignment_viewer.viewer.anchorClicked.connect(self.viewer_link_clicked)
|
|
53
|
-
|
|
54
|
-
self.pages_viewer = PagesViewer(self.link_transformer)
|
|
55
|
-
self.pages_viewer.viewer.anchorClicked.connect(self.viewer_link_clicked)
|
|
56
|
-
|
|
57
|
-
self.file_viewer = FileViewTab(data_manager.download_pool)
|
|
58
|
-
self.file_viewer.files_column.tree.itemActivated.connect(self.download_file_from_file_pane)
|
|
59
|
-
self.file_viewer.assignment_files_column.tree.itemActivated.connect(self.download_file_from_file_pane)
|
|
60
|
-
|
|
61
|
-
self.tab_widget = QTabWidget()
|
|
62
|
-
self.tab_widget.insertTab(0, self.file_viewer, "Files")
|
|
63
|
-
self.tab_widget.insertTab(1, self.assignment_viewer, "Assignments")
|
|
64
|
-
self.tab_widget.insertTab(2, self.pages_viewer, "Pages")
|
|
65
|
-
|
|
66
|
-
self.course_name_label = QLabel(_no_course_selected_text)
|
|
67
|
-
self.course_name_label.setStyleSheet("font-weight: bold;")
|
|
68
|
-
course_viewer_layout = self.create_layout_and_add_widgets(QVBoxLayout, self.course_name_label, self.tab_widget)
|
|
69
|
-
|
|
70
|
-
v_layout = self.create_layout_and_add_widgets(QVBoxLayout, self.course_list, self.sync_button)
|
|
71
|
-
|
|
72
|
-
widget = QWidget()
|
|
73
|
-
h_layout = self.create_layout_and_add_widgets(QHBoxLayout, v_layout, course_viewer_layout)
|
|
74
|
-
h_layout.setStretch(1, 1)
|
|
75
|
-
widget.setLayout(h_layout)
|
|
76
|
-
|
|
77
|
-
self.setCentralWidget(widget)
|
|
78
|
-
|
|
79
|
-
self.setup_menu_bar()
|
|
80
|
-
|
|
81
|
-
self.files_grouping_preference_changed.connect(self.on_grouping_preference_changed)
|
|
82
|
-
|
|
83
|
-
self.loaded.connect(self.load_course_list)
|
|
84
|
-
self.loaded.connect(self.check_for_update)
|
|
85
|
-
self.loaded.emit()
|
|
86
|
-
|
|
87
|
-
self.restore_window_position()
|
|
88
|
-
|
|
89
|
-
# Activate the statusbar so it doesn't just appear randomly later
|
|
90
|
-
bar: QStatusBar = self.statusBar()
|
|
91
|
-
# Set its height so it doesn't get bigger when there's a progress bar in it
|
|
92
|
-
bar.setFixedHeight(bar.height())
|
|
93
|
-
|
|
94
|
-
@staticmethod
|
|
95
|
-
def create_layout_and_add_widgets(layout_type: type, *widgets) -> QLayout:
|
|
96
|
-
layout = layout_type()
|
|
97
|
-
|
|
98
|
-
for widget in widgets:
|
|
99
|
-
if isinstance(widget, QLayout):
|
|
100
|
-
layout.addLayout(widget)
|
|
101
|
-
else:
|
|
102
|
-
layout.addWidget(widget)
|
|
103
|
-
|
|
104
|
-
return layout
|
|
105
|
-
|
|
106
|
-
def setup_menu_bar(self):
|
|
107
|
-
menu_bar = self.menuBar()
|
|
108
|
-
|
|
109
|
-
app_menu: QMenu = menu_bar.addMenu("App")
|
|
110
|
-
view_menu: QMenu = menu_bar.addMenu("View")
|
|
111
|
-
|
|
112
|
-
app_menu.addAction(self.setup_quick_authentication_action(app_menu))
|
|
113
|
-
app_menu.addMenu(ThemeSelectionMenu())
|
|
114
|
-
view_menu.addMenu(self.setup_group_by_menu())
|
|
115
|
-
|
|
116
|
-
def setup_group_by_menu(self) -> QMenu:
|
|
117
|
-
file_grouping_menu = GroupingPreferencesMenu(self.data_manager)
|
|
118
|
-
self.course_list.course_selected.connect(file_grouping_menu.course_changed)
|
|
119
|
-
file_grouping_menu.preference_changed.connect(self.on_grouping_preference_changed)
|
|
120
|
-
|
|
121
|
-
return file_grouping_menu
|
|
122
|
-
|
|
123
|
-
def setup_quick_authentication_action(self, parent: QObject):
|
|
124
|
-
return create_qaction(
|
|
125
|
-
name="Quick canvas login",
|
|
126
|
-
shortcut=QKeySequence("Ctrl+O"),
|
|
127
|
-
triggered=self.open_quick_auth_in_browser,
|
|
128
|
-
parent=parent
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
@asyncSlot()
|
|
132
|
-
async def open_quick_auth_in_browser(self):
|
|
133
|
-
opening_progress_dialog = QProgressDialog("Opening canvas", None, 0, 0, self)
|
|
134
|
-
opening_progress_dialog.setWindowTitle("Please wait")
|
|
135
|
-
opening_progress_dialog.show()
|
|
136
|
-
QDesktopServices.openUrl(await self.data_manager.client.get_temp_session_link())
|
|
137
|
-
opening_progress_dialog.close()
|
|
138
|
-
|
|
139
|
-
def closeEvent(self, event):
|
|
140
|
-
settings.geometry = self.saveGeometry()
|
|
141
|
-
settings.window_state = self.saveState()
|
|
142
|
-
|
|
143
|
-
def restore_window_position(self):
|
|
144
|
-
self.restoreGeometry(settings.geometry)
|
|
145
|
-
self.restoreState(settings.window_state)
|
|
146
|
-
|
|
147
|
-
@asyncSlot(QUrl)
|
|
148
|
-
async def viewer_link_clicked(self, url: QUrl):
|
|
149
|
-
# The url of a transformed link will start with a specific prefix
|
|
150
|
-
if url.toString().startswith(LinkTransformer.transformed_url_prefix):
|
|
151
|
-
# The rest of the 'url' is just the file id
|
|
152
|
-
resource = self.resources[url.toString().removeprefix(LinkTransformer.transformed_url_prefix)]
|
|
153
|
-
|
|
154
|
-
await self.data_manager.download_resource(resource)
|
|
155
|
-
QDesktopServices.openUrl(QUrl.fromLocalFile(resource.download_location.absolute()))
|
|
156
|
-
else:
|
|
157
|
-
QDesktopServices.openUrl(url)
|
|
158
|
-
|
|
159
|
-
@asyncSlot(QTreeWidgetItem, int)
|
|
160
|
-
async def download_file_from_file_pane(self, item: QTreeWidgetItem, _: int):
|
|
161
|
-
if isinstance(item, FileRow):
|
|
162
|
-
await self.data_manager.download_resource(item.resource)
|
|
163
|
-
QDesktopServices.openUrl(QUrl.fromLocalFile(item.resource.download_location.absolute()))
|
|
164
|
-
|
|
165
|
-
@asyncSlot()
|
|
166
|
-
async def sync_data(self):
|
|
167
|
-
self.sync_button.setEnabled(False)
|
|
168
|
-
self.sync_button.setText("Synchronizing")
|
|
169
|
-
try:
|
|
170
|
-
await self.data_manager.synchronize_with_canvas(StatusBarReporter(self.statusBar()))
|
|
171
|
-
await self.load_course_list()
|
|
172
|
-
|
|
173
|
-
finally:
|
|
174
|
-
self.sync_button.setEnabled(True)
|
|
175
|
-
self.sync_button.setText("Synchronize")
|
|
176
|
-
|
|
177
|
-
@asyncSlot()
|
|
178
|
-
async def load_course_list(self):
|
|
179
|
-
self.courses = (await self.data_manager.get_data())
|
|
180
|
-
self.selected_course = None
|
|
181
|
-
self.resources.clear()
|
|
182
|
-
|
|
183
|
-
for course in self.courses:
|
|
184
|
-
self.resources.update({resource.id: resource for resource in course.resources})
|
|
185
|
-
|
|
186
|
-
self.course_list.load_course_list(self.courses)
|
|
187
|
-
|
|
188
|
-
@asyncSlot()
|
|
189
|
-
async def check_for_update(self):
|
|
190
|
-
try:
|
|
191
|
-
newer_version, installed_version = await self_updater.get_newer_version()
|
|
192
|
-
|
|
193
|
-
if newer_version is not None and newer_version != settings.ignored_update:
|
|
194
|
-
msg_box = QMessageBox(
|
|
195
|
-
QMessageBox.Icon.Question,
|
|
196
|
-
"Update available",
|
|
197
|
-
f"There is an update available ({installed_version} -> {newer_version})\nDo you want to update?\nThe program will close after the update is finished.",
|
|
198
|
-
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
|
199
|
-
self
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
def ignore_update():
|
|
203
|
-
settings.ignored_update = newer_version
|
|
204
|
-
|
|
205
|
-
msg_box.accepted.connect(self.do_self_update)
|
|
206
|
-
msg_box.rejected.connect(ignore_update)
|
|
207
|
-
msg_box.show()
|
|
208
|
-
else:
|
|
209
|
-
print("No update available (or skipping this update)")
|
|
210
|
-
except BaseException as e:
|
|
211
|
-
sys.stderr.write(f"Could not check for updates: {e}\n")
|
|
212
|
-
traceback.print_exc()
|
|
213
|
-
sys.stderr.write("This can be ignored if in a dev environment\n")
|
|
214
|
-
|
|
215
|
-
@asyncSlot()
|
|
216
|
-
async def do_self_update(self):
|
|
217
|
-
try:
|
|
218
|
-
progress_diag = QProgressDialog("Updating", None, 0, 0, self)
|
|
219
|
-
progress_diag.setWindowTitle(app_name)
|
|
220
|
-
progress_diag.show()
|
|
221
|
-
await self_updater.do_update()
|
|
222
|
-
self.close()
|
|
223
|
-
except BaseException as e:
|
|
224
|
-
traceback.print_exc()
|
|
225
|
-
|
|
226
|
-
QMessageBox(
|
|
227
|
-
QMessageBox.Icon.Critical,
|
|
228
|
-
"Error",
|
|
229
|
-
"An error occurred during the update",
|
|
230
|
-
parent=self
|
|
231
|
-
).show()
|
|
232
|
-
|
|
233
|
-
@Slot(db.Course)
|
|
234
|
-
def on_course_selected(self, course: Optional[db.Course]):
|
|
235
|
-
if course is not None:
|
|
236
|
-
self.selected_course = course
|
|
237
|
-
# todo these should really be slots connected to this signal...
|
|
238
|
-
self.pages_viewer.fill_tree(course)
|
|
239
|
-
self.assignment_viewer.fill_tree(course)
|
|
240
|
-
self.file_viewer.load_course_files(course)
|
|
241
|
-
self.course_name_label.setText(course.name)
|
|
242
|
-
else:
|
|
243
|
-
self.selected_course = None
|
|
244
|
-
self.file_viewer.clear()
|
|
245
|
-
self.pages_viewer.clear()
|
|
246
|
-
self.assignment_viewer.clear()
|
|
247
|
-
self.course_name_label.setText(_no_course_selected_text)
|
|
248
|
-
|
|
249
|
-
@asyncSlot(db.CoursePreferences)
|
|
250
|
-
async def on_grouping_preference_changed(self):
|
|
251
|
-
self.file_viewer.load_course_files(self.selected_course)
|
qcanvas/ui/menu_bar/__init__.py
DELETED
|
File without changes
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
from PySide6.QtCore import Slot, Signal
|
|
2
|
-
from PySide6.QtWidgets import QMenu, QWidget
|
|
3
|
-
from qasync import asyncSlot
|
|
4
|
-
|
|
5
|
-
import qcanvas.db as db
|
|
6
|
-
from qcanvas.util.course_indexer import DataManager
|
|
7
|
-
from qcanvas.util.helpers.qaction_helper import create_qaction
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class GroupingPreferencesMenu(QMenu):
|
|
11
|
-
_preference_changed_private = Signal(db.GroupByPreference)
|
|
12
|
-
preference_changed = Signal()
|
|
13
|
-
|
|
14
|
-
def __init__(self, data_manager: DataManager, parent: QWidget | None = None):
|
|
15
|
-
super().__init__("Group files by", parent)
|
|
16
|
-
self.setEnabled(False)
|
|
17
|
-
|
|
18
|
-
self.data_manager = data_manager
|
|
19
|
-
self._selected_course: db.Course | None = None
|
|
20
|
-
|
|
21
|
-
self.group_by_modules_action = self._make_action("Modules", db.GroupByPreference.GROUP_BY_MODULES)
|
|
22
|
-
self.group_by_pages_action = self._make_action("Pages", db.GroupByPreference.GROUP_BY_PAGES)
|
|
23
|
-
|
|
24
|
-
self.addActions([self.group_by_pages_action, self.group_by_modules_action])
|
|
25
|
-
|
|
26
|
-
self._preference_changed_private.connect(self._on_preference_changed)
|
|
27
|
-
|
|
28
|
-
def _make_action(self, text: str, preference_value: db.GroupByPreference):
|
|
29
|
-
return create_qaction(
|
|
30
|
-
name=text,
|
|
31
|
-
parent=self,
|
|
32
|
-
triggered=lambda: self._preference_changed_private.emit(preference_value),
|
|
33
|
-
checkable=True,
|
|
34
|
-
checked=False
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
@Slot()
|
|
38
|
-
def course_changed(self, course: db.Course | None):
|
|
39
|
-
if course is not None:
|
|
40
|
-
self._selected_course = course
|
|
41
|
-
self._update_actions()
|
|
42
|
-
self.setEnabled(True)
|
|
43
|
-
else:
|
|
44
|
-
self._selected_course = None
|
|
45
|
-
self.setEnabled(False)
|
|
46
|
-
|
|
47
|
-
@asyncSlot()
|
|
48
|
-
async def _on_preference_changed(self, preference: db.GroupByPreference):
|
|
49
|
-
if self._selected_course is not None:
|
|
50
|
-
self._selected_course.preferences.files_group_by_preference = preference
|
|
51
|
-
self._update_actions()
|
|
52
|
-
# todo not sure if this should go in main_ui or here...
|
|
53
|
-
await self.data_manager.update_item(self._selected_course.preferences)
|
|
54
|
-
|
|
55
|
-
self.preference_changed.emit()
|
|
56
|
-
|
|
57
|
-
def _update_actions(self):
|
|
58
|
-
preference = self._selected_course.preferences.files_group_by_preference
|
|
59
|
-
|
|
60
|
-
self.group_by_pages_action.setChecked(preference == db.GroupByPreference.GROUP_BY_PAGES)
|
|
61
|
-
self.group_by_modules_action.setChecked(preference == db.GroupByPreference.GROUP_BY_MODULES)
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
from PySide6.QtGui import QActionGroup
|
|
2
|
-
from PySide6.QtWidgets import QMenu, QWidget
|
|
3
|
-
|
|
4
|
-
from qcanvas.util.app_settings import settings
|
|
5
|
-
from qcanvas.util.helpers import theme_helper
|
|
6
|
-
from qcanvas.util.helpers.qaction_helper import create_qaction
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def change_theme(theme_name: str):
|
|
10
|
-
settings.theme = theme_name
|
|
11
|
-
theme_helper.apply_selected_theme()
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class ThemeSelectionMenu(QMenu):
|
|
15
|
-
def __init__(self, parent: QWidget | None = None):
|
|
16
|
-
super().__init__("Theme", parent)
|
|
17
|
-
|
|
18
|
-
action_group = QActionGroup(self)
|
|
19
|
-
|
|
20
|
-
light_theme = self._create_action("Light", "light")
|
|
21
|
-
dark_theme = self._create_action("Dark", "dark")
|
|
22
|
-
auto_theme = self._create_action("Auto (YMMV)", "auto")
|
|
23
|
-
native_theme = self._create_action("Native (requires restart)", "native")
|
|
24
|
-
|
|
25
|
-
actions = [light_theme, dark_theme, auto_theme, native_theme]
|
|
26
|
-
|
|
27
|
-
self.addActions(actions)
|
|
28
|
-
|
|
29
|
-
for theme in actions:
|
|
30
|
-
action_group.addAction(theme)
|
|
31
|
-
|
|
32
|
-
def _create_action(self, text: str, theme_name: str):
|
|
33
|
-
return create_qaction(
|
|
34
|
-
name=text,
|
|
35
|
-
parent=self,
|
|
36
|
-
triggered=lambda: change_theme(theme_name),
|
|
37
|
-
checkable=True,
|
|
38
|
-
checked=settings.theme == theme_name
|
|
39
|
-
)
|