qcanvas 1.0.12.dev2__py3-none-any.whl → 1.1.0__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/run.py CHANGED
@@ -1,3 +1,27 @@
1
+ # nuitka-project: --enable-plugin=pyside6
2
+ # nuitka-project: --windows-icon-from-ico=./deploy/windows/qcanvas.ico
3
+ # nuitka-project: --windows-console-mode=attach
4
+ # nuitka-project: --linux-icon=deploy/appimage/qcanvas.svg
5
+ # nuitka-project: --standalone
6
+ # nuitka-project: --include-module=yt_dlp
7
+
8
+ # Anti-bloat
9
+
10
+ ##########################################################
11
+ # nuitka-project: --include-package=aiosqlite
12
+ # nuitka-project: --nofollow-import-to=aiosqlite.tests.*
13
+ ##########################################################
14
+ # nuitka-project: --nofollow-import-to=rich
15
+ ##########################################################
16
+ # nuitka-project: --nofollow-import-to=sqlalchemy.dialects.mssql
17
+ # nuitka-project: --nofollow-import-to=sqlalchemy.dialects.postgresql
18
+ # nuitka-project: --nofollow-import-to=sqlalchemy.dialects.oracle
19
+ # nuitka-project: --nofollow-import-to=sqlalchemy.dialects.mysql
20
+ ##########################################################
21
+ # nuitka-project: --nofollow-import-to=rich
22
+ ##########################################################
23
+ # nuitka-project: --nofollow-import-to=yt_dlp.extractor.lazy_extractors
24
+
1
25
  import logging
2
26
  from logging import INFO, WARNING
3
27
 
@@ -45,8 +45,8 @@ class ContentTree(MemoryTreeWidget, Generic[T]):
45
45
  *,
46
46
  header_text: str | Sequence[str],
47
47
  indentation: int = 20,
48
- max_width: int,
49
- min_width: int,
48
+ max_width: Optional[int] = None,
49
+ min_width: Optional[int] = None,
50
50
  ) -> None:
51
51
  if not isinstance(header_text, str) and isinstance(header_text, Sequence):
52
52
  self.setHeaderLabels(header_text)
@@ -54,8 +54,12 @@ class ContentTree(MemoryTreeWidget, Generic[T]):
54
54
  self.setHeaderLabel(header_text)
55
55
 
56
56
  self.setIndentation(indentation)
57
- self.setMaximumWidth(max_width)
58
- self.setMinimumWidth(min_width)
57
+
58
+ if max_width is not None:
59
+ self.setMaximumWidth(max_width)
60
+
61
+ if min_width is not None:
62
+ self.setMinimumWidth(min_width)
59
63
 
60
64
  def reload(self, data: T, *, sync_receipt: SyncReceipt) -> None:
61
65
  self._reloading = True
@@ -47,18 +47,12 @@ class CourseViewer(QWidget):
47
47
  downloader=downloader,
48
48
  sync_receipt=sync_receipt,
49
49
  )
50
- # self._files_tab = FileTab.create_from_receipt(
51
- # course=course,
52
- # downloader=downloader,
53
- # sync_receipt=sync_receipt,
54
- # )
55
50
 
56
51
  self._tabs = QTabWidget()
57
52
  self._tabs.addTab(self._pages_tab, "Pages")
58
53
  self._tabs.addTab(self._assignments_tab, "Assignments")
59
54
  self._tabs.addTab(self._mail_tab, "Mail")
60
55
  self._tabs.addTab(QLabel("Not implemented"), "Files")
61
- # self._tabs.addTab(self._files_tab, "Files")
62
56
  # self._tabs.addTab(QLabel("Not implemented"), "Panopto") # The meme lives on!
63
57
 
64
58
  self.setLayout(layout(QVBoxLayout, self._course_label, self._tabs))
@@ -84,10 +78,6 @@ class CourseViewer(QWidget):
84
78
  updates = sync_receipt.updates_by_course.get(self._course_id, None)
85
79
 
86
80
  if updates is not None:
87
- # if len(updates.updated_resources) > 0:
88
- # raise Exception("Looks like you forgot to update the other numbers??????"")
89
- # self._highlight_tab(0)
90
-
91
81
  if len(updates.updated_pages) > 0:
92
82
  self._highlight_tab(0)
93
83
 
@@ -50,12 +50,6 @@ class ResourceRichBrowser(QTextBrowser):
50
50
  self._downloader.download_finished.connect(self._download_updated)
51
51
  self._downloader.download_failed.connect(self._download_updated)
52
52
 
53
- # _dark_listener.theme_changed.connect(self._theme_changed)
54
-
55
- # @Slot()
56
- # def _theme_changed(self, theme: str) -> None:
57
- # print(theme)
58
-
59
53
  def show_blank(self, completely_blank: bool = False) -> None:
60
54
  if completely_blank:
61
55
  self.clear()
@@ -102,16 +96,31 @@ class ResourceRichBrowser(QTextBrowser):
102
96
  )
103
97
  continue
104
98
 
105
- file_link_tag = self._create_resource_link_tag(doc, resource_id)
99
+ file_link_tag = self._create_resource_link_tag(
100
+ doc, resource_id, resource_link.name == "img"
101
+ )
106
102
  resource_link.replace_with(file_link_tag)
107
103
  except NoExtractorError:
108
104
  pass
109
105
 
110
106
  return str(doc)
111
107
 
112
- def _create_resource_link_tag(self, doc: BeautifulSoup, resource_id: str) -> Tag:
108
+ def _create_resource_link_tag(
109
+ self, doc: BeautifulSoup, resource_id: str, is_image: bool
110
+ ) -> Tag:
113
111
  resource = self._current_content_resources[resource_id]
114
112
 
113
+ # todo not sure if this is a good idea or not
114
+ # if is_image and resource.download_state == db.ResourceDownloadState.DOWNLOADED:
115
+ # location = self._downloader.resource_download_location(resource)
116
+ #
117
+ # file_link_tag = doc.new_tag(
118
+ # "img",
119
+ # attrs={
120
+ # "source": location.absolute(),
121
+ # },
122
+ # )
123
+ # else:
115
124
  file_link_tag = doc.new_tag(
116
125
  "a",
117
126
  attrs={
@@ -23,7 +23,7 @@ from qcanvas.ui.main_ui.options.quick_sync_option import QuickSyncOption
23
23
  from qcanvas.ui.main_ui.options.sync_on_start_option import SyncOnStartOption
24
24
  from qcanvas.ui.main_ui.options.theme_selection_menu import ThemeSelectionMenu
25
25
  from qcanvas.ui.main_ui.status_bar_progress_display import StatusBarProgressDisplay
26
- from qcanvas.util import paths, settings, auto_downloader
26
+ from qcanvas.util import auto_downloader, paths, settings
27
27
  from qcanvas.util.qurl_util import file_url
28
28
  from qcanvas.util.ui_tools import create_qaction
29
29
 
@@ -1,13 +1,13 @@
1
1
  import logging
2
2
 
3
3
  import qcanvas.util.settings as settings
4
- from qcanvas.util import is_url
4
+ from qcanvas.util.url_checker import is_url
5
5
 
6
6
  _logger = logging.getLogger(__name__)
7
7
 
8
8
 
9
9
  def needs_setup() -> bool:
10
- if not is_url(settings.client.panopto_url):
10
+ if not settings.client.panopto_disabled and not is_url(settings.client.panopto_url):
11
11
  return True
12
12
  elif not is_url(settings.client.canvas_url):
13
13
  return True
@@ -1,18 +1,19 @@
1
1
  import logging
2
2
  from threading import Semaphore
3
+ from typing import Optional
3
4
 
4
5
  from qasync import asyncSlot
5
6
  from qcanvas_api_clients.canvas import CanvasClient, CanvasClientConfig
6
7
  from qcanvas_api_clients.panopto import PanoptoClient, PanoptoClientConfig
7
8
  from qcanvas_api_clients.util.request_exceptions import ConfigInvalidError
8
- from qtpy.QtCore import QUrl, Signal, Slot
9
+ from qtpy.QtCore import Qt, QUrl, Signal, Slot
9
10
  from qtpy.QtGui import QDesktopServices, QIcon
10
11
  from qtpy.QtWidgets import *
11
12
 
12
13
  import qcanvas.util.settings as settings
13
14
  from qcanvas import icons
14
- from qcanvas.util import is_url
15
- from qcanvas.util.layouts import grid_layout_widget, layout
15
+ from qcanvas.util.layouts import GridItem, grid_layout_widget, layout
16
+ from qcanvas.util.url_checker import is_url
16
17
 
17
18
  _logger = logging.getLogger(__name__)
18
19
 
@@ -21,6 +22,68 @@ _tutorial_url = (
21
22
  )
22
23
 
23
24
 
25
+ class _InputRow:
26
+ def __init__(
27
+ self,
28
+ *,
29
+ label: str,
30
+ initial_value: str,
31
+ placeholder_text: Optional[str] = None,
32
+ is_password: bool = False,
33
+ ):
34
+ self._label = QLabel(label)
35
+ self._input = QLineEdit(initial_value)
36
+
37
+ if placeholder_text is not None:
38
+ self._input.setPlaceholderText(placeholder_text)
39
+
40
+ if is_password:
41
+ self._input.setEchoMode(QLineEdit.EchoMode.Password)
42
+
43
+ def set_error(self, message: Optional[str]) -> None:
44
+ self._input.setStyleSheet("QLineEdit { border: 1px solid red }")
45
+ self._input.setToolTip(message)
46
+
47
+ def clear_error(self) -> None:
48
+ self._input.setStyleSheet(None)
49
+ self._input.setToolTip(None)
50
+
51
+ def grid_row(self) -> list[QWidget]:
52
+ return [self._label, self._input]
53
+
54
+ def disable(self) -> None:
55
+ self._input.setEnabled(False)
56
+
57
+ @property
58
+ def enabled(self) -> bool:
59
+ return self._input.isEnabled()
60
+
61
+ @enabled.setter
62
+ def enabled(self, value: bool) -> None:
63
+ self._input.setEnabled(value)
64
+
65
+ @property
66
+ def text(self) -> str:
67
+ return self._input.text().strip()
68
+
69
+ @property
70
+ def url_text(self) -> str:
71
+ url = self.text
72
+
73
+ if not url.startswith("http"):
74
+ return "https://" + url
75
+ else:
76
+ return url
77
+
78
+ @property
79
+ def is_valid_url(self) -> bool:
80
+ return is_url(self.url_text)
81
+
82
+ @property
83
+ def is_empty(self) -> bool:
84
+ return len(self.text) == 0
85
+
86
+
24
87
  class SetupDialog(QDialog):
25
88
  closed = Signal()
26
89
 
@@ -33,38 +96,58 @@ class SetupDialog(QDialog):
33
96
  self.setWindowIcon(QIcon(icons.main_icon))
34
97
 
35
98
  self._semaphore = Semaphore()
36
- self._canvas_url_box = QLineEdit(settings.client.canvas_url)
37
- self._canvas_url_box.setPlaceholderText("https://instance.canvas.com")
38
- self._canvas_api_key_box = QLineEdit(settings.client.canvas_api_key)
39
- self._canvas_api_key_box.setEchoMode(QLineEdit.EchoMode.Password)
40
- self._panopto_url_box = QLineEdit(settings.client.panopto_url)
41
- self._panopto_url_box.setPlaceholderText("https://instance.panopto.com")
99
+
100
+ self._canvas_url_box = _InputRow(
101
+ label="Canvas URL",
102
+ initial_value=settings.client.canvas_url,
103
+ placeholder_text="https://instance.canvas.com",
104
+ )
105
+ self._panopto_url_box = _InputRow(
106
+ label="Panopto URL",
107
+ initial_value=settings.client.panopto_url,
108
+ placeholder_text="https://instance.panopto.com",
109
+ )
110
+ self._canvas_api_key_box = _InputRow(
111
+ label="Canvas API Key",
112
+ initial_value=settings.client.canvas_api_key,
113
+ is_password=True,
114
+ )
115
+ self._disable_panopto_checkbox = QCheckBox("Continue without Panopto")
116
+ self._disable_panopto_checkbox.checkStateChanged.connect(
117
+ self._disable_panopto_check_changed
118
+ )
42
119
  self._button_box = self._setup_button_box()
43
- self._button_box.accepted.connect(self._accepted)
44
- self._button_box.helpRequested.connect(self._help_requested)
45
120
  self._waiting_indicator = self._setup_progress_bar()
46
- self._status_bar = QStatusBar()
47
121
 
48
122
  self.setLayout(
49
123
  layout(
50
124
  QVBoxLayout,
51
125
  grid_layout_widget(
52
126
  [
53
- [QLabel("Canvas URL"), self._canvas_url_box],
54
- [QLabel("Canvas API Key"), self._canvas_api_key_box],
55
- [QLabel("Panopto URL"), self._panopto_url_box],
127
+ self._canvas_url_box.grid_row(),
128
+ self._canvas_api_key_box.grid_row(),
129
+ self._panopto_url_box.grid_row(),
130
+ [
131
+ GridItem(
132
+ self._disable_panopto_checkbox,
133
+ col_span=2,
134
+ alignment=Qt.AlignmentFlag.AlignRight,
135
+ )
136
+ ],
56
137
  ]
57
138
  ),
58
139
  self._waiting_indicator,
59
140
  self._button_box,
60
- self._status_bar,
61
141
  )
62
142
  )
63
143
 
64
144
  def _setup_button_box(self) -> QDialogButtonBox:
65
145
  box = QDialogButtonBox()
66
146
  box.addButton(QDialogButtonBox.StandardButton.Ok)
67
- box.addButton("Get a Canvas API key", QDialogButtonBox.ButtonRole.HelpRole)
147
+ box.addButton("Get a Canvas API Key", QDialogButtonBox.ButtonRole.HelpRole)
148
+
149
+ box.accepted.connect(self._verify_settings)
150
+ box.helpRequested.connect(self._help_requested)
68
151
  return box
69
152
 
70
153
  def _setup_progress_bar(self) -> QProgressBar:
@@ -81,32 +164,33 @@ class SetupDialog(QDialog):
81
164
  widget.setSizePolicy(size_policy)
82
165
 
83
166
  @asyncSlot()
84
- async def _accepted(self) -> None:
167
+ async def _verify_settings(self) -> None:
85
168
  if self._semaphore.acquire(False):
86
169
  try:
87
170
  self._clear_errors()
88
171
 
89
- if not self._all_inputs_valid():
90
- self._status_bar.showMessage("Invalid input!", 5000)
172
+ if not self._check_all_inputs():
91
173
  return
92
174
 
93
175
  self._waiting_indicator.setVisible(True)
94
- self._status_bar.showMessage("Checking configuration...")
95
176
 
96
177
  canvas_config = CanvasClientConfig(
97
- api_token=self._canvas_api_key_box.text().strip(),
98
- canvas_url=self._get_url(self._canvas_url_box),
178
+ api_token=self._canvas_api_key_box.text,
179
+ canvas_url=self._canvas_url_box.url_text,
99
180
  )
100
181
 
101
182
  if not await self._check_canvas_config(canvas_config):
102
183
  return
103
184
 
104
- if not await self._check_panopto_config(canvas_config):
105
- self._show_panopto_help()
106
- return
185
+ if self._panopto_enabled:
186
+ if not await self._check_panopto_config(canvas_config):
187
+ self._show_panopto_help()
188
+ return
107
189
  except Exception as e:
108
- self._status_bar.showMessage(f"An error occurred: {e}", 5000)
109
190
  _logger.warning("Checking config failed", exc_info=e)
191
+
192
+ error_box = QErrorMessage(self)
193
+ error_box.showMessage(f"Checking config failed: {e}")
110
194
  finally:
111
195
  self._waiting_indicator.setVisible(False)
112
196
  self._semaphore.release()
@@ -117,59 +201,40 @@ class SetupDialog(QDialog):
117
201
  _logger.debug("Validation already in progress")
118
202
 
119
203
  def _clear_errors(self) -> None:
120
- for line_edit in [
121
- self._canvas_url_box,
122
- self._panopto_url_box,
123
- self._canvas_api_key_box,
124
- ]:
125
- self._status_bar.clearMessage()
126
- line_edit.setStyleSheet(None)
127
- line_edit.setToolTip(None)
128
-
129
- def _all_inputs_valid(self) -> bool:
204
+ self._canvas_url_box.clear_error()
205
+ self._panopto_url_box.clear_error()
206
+ self._canvas_api_key_box.clear_error()
207
+
208
+ def _check_all_inputs(self) -> bool:
130
209
  all_valid = True
131
210
 
132
- if not is_url(self._get_url(self._canvas_url_box)):
211
+ if not self._canvas_url_box.is_valid_url:
133
212
  all_valid = False
134
- self._show_error(self._canvas_url_box, "Canvas URL is invalid")
135
- if len(self._canvas_api_key_box.text().strip()) == 0:
213
+ self._canvas_url_box.set_error("Canvas URL is invalid")
214
+
215
+ if self._canvas_api_key_box.is_empty:
136
216
  all_valid = False
137
- self._show_error(self._canvas_api_key_box, "Canvas API key is empty")
138
- if not is_url(self._get_url(self._panopto_url_box)):
217
+ self._canvas_api_key_box.set_error("Canvas API key is empty")
218
+
219
+ if self._panopto_enabled and not self._panopto_url_box.is_valid_url:
139
220
  all_valid = False
140
- self._show_error(self._panopto_url_box, "Panopto URL is invalid")
221
+ self._panopto_url_box.set_error("Panopto URL is invalid")
141
222
 
142
223
  return all_valid
143
224
 
144
- def _get_url(self, line_edit: QLineEdit) -> str:
145
- url = line_edit.text().strip()
146
-
147
- if not url.startswith("http"):
148
- return "https://" + url
149
- else:
150
- return url
151
-
152
225
  async def _check_canvas_config(self, canvas_config: CanvasClientConfig) -> bool:
153
226
  try:
154
227
  await CanvasClient.verify_config(canvas_config)
155
228
  return True
156
229
  except ConfigInvalidError:
157
- self._show_error(self._canvas_api_key_box, "Canvas API key is invalid")
230
+ self._canvas_api_key_box.set_error("Canvas API key is invalid")
158
231
  return False
159
232
 
160
- def _show_error(self, line_edit: QLineEdit, text: str) -> None:
161
- line_edit.setToolTip(text)
162
- self._waiting_indicator.hide()
163
- self._highlight_line_edit(line_edit)
164
-
165
- def _highlight_line_edit(self, line_edit: QLineEdit) -> None:
166
- line_edit.setStyleSheet("QLineEdit { border: 1px solid red }")
167
-
168
233
  async def _check_panopto_config(self, canvas_config: CanvasClientConfig) -> bool:
169
234
  client = CanvasClient(canvas_config)
170
235
  try:
171
236
  await PanoptoClient.verify_config(
172
- PanoptoClientConfig(panopto_url=self._get_url(self._panopto_url_box)),
237
+ PanoptoClientConfig(panopto_url=self._panopto_url_box.url_text),
173
238
  client,
174
239
  )
175
240
  return True
@@ -194,15 +259,21 @@ class SetupDialog(QDialog):
194
259
 
195
260
  @Slot()
196
261
  def _open_panopto_login(self) -> None:
197
- url = QUrl(self._get_url(self._panopto_url_box))
262
+ url = QUrl(self._panopto_url_box.url_text)
198
263
  url.setPath("/Panopto/Pages/Auth/Login.aspx")
199
264
  url.setQuery("instance=Canvas&AllowBounce=true")
200
265
  QDesktopServices.openUrl(url)
201
266
 
202
267
  def _save_and_close(self) -> None:
203
- settings.client.canvas_url = self._get_url(self._canvas_url_box)
204
- settings.client.panopto_url = self._get_url(self._panopto_url_box)
205
- settings.client.canvas_api_key = self._canvas_api_key_box.text().strip()
268
+ settings.client.canvas_url = self._canvas_url_box.url_text
269
+
270
+ if self._panopto_enabled:
271
+ settings.client.panopto_url = self._panopto_url_box.url_text
272
+ else:
273
+ settings.client.panopto_disabled = True
274
+
275
+ settings.client.canvas_api_key = self._canvas_api_key_box.text
276
+
206
277
  self.closed.emit()
207
278
  self.close()
208
279
 
@@ -223,3 +294,11 @@ class SetupDialog(QDialog):
223
294
  @Slot()
224
295
  def _open_tutorial(self) -> None:
225
296
  QDesktopServices.openUrl(QUrl(_tutorial_url))
297
+
298
+ @Slot(Qt.CheckState)
299
+ def _disable_panopto_check_changed(self, state: Qt.CheckState) -> None:
300
+ self._panopto_url_box.enabled = state == Qt.CheckState.Unchecked
301
+
302
+ @property
303
+ def _panopto_enabled(self) -> bool:
304
+ return self._disable_panopto_checkbox.checkState() == Qt.CheckState.Unchecked
qcanvas/util/__init__.py CHANGED
@@ -1,2 +0,0 @@
1
- # todo remove this
2
- from .url_checker import is_url
@@ -4,8 +4,7 @@ import logging
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.QtWidgets import QMessageBox
8
- from qtpy.QtWidgets import QWidget
7
+ from qtpy.QtWidgets import QMessageBox, QWidget
9
8
 
10
9
  from qcanvas.util import settings
11
10
 
qcanvas/util/paths.py CHANGED
@@ -1,30 +1,27 @@
1
1
  import logging
2
2
  import os
3
- import platform
4
- import sys
5
3
  from pathlib import Path
6
4
 
7
5
  import cachetools
8
6
  import platformdirs
9
7
  from qtpy.QtCore import QSettings
10
8
 
9
+ from qcanvas.util.runtime import *
10
+
11
11
  _logger = logging.getLogger(__name__)
12
12
 
13
- _is_running_portable = Path(".portable").exists()
14
- _is_running_on_windows = platform.system() == "Windows"
15
- _is_running_on_linux = platform.system() == "Linux"
16
- _is_running_as_pyinstaller = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
17
- _is_running_as_flatpak = os.environ.get("container", "") == "flatpak"
13
+
14
+ def ui_storage() -> Path:
15
+ return root() / ".UI"
16
+
17
+
18
+ def data_storage() -> Path:
19
+ return root()
18
20
 
19
21
 
20
22
  def client_settings() -> QSettings:
21
- if _is_running_portable:
23
+ if is_running_portable:
22
24
  return QSettings("QCanvas.ini", QSettings.Format.IniFormat)
23
- elif _is_running_as_pyinstaller and _is_running_on_windows:
24
- return QSettings(
25
- str(platformdirs.user_documents_path() / "QCanvasTeam" / "QCanvas.ini"),
26
- QSettings.Format.IniFormat,
27
- )
28
25
  else:
29
26
  return QSettings("QCanvasTeam", "QCanvas")
30
27
 
@@ -33,20 +30,12 @@ def client_settings() -> QSettings:
33
30
  def root() -> Path:
34
31
  root_path = Path()
35
32
 
36
- if not _is_running_portable:
37
- if _is_running_as_flatpak:
38
- root_path = Path(os.environ["XDG_DATA_HOME"])
39
- elif _is_running_as_pyinstaller:
40
- root_path = platformdirs.user_data_path("QCanvasReborn", "QCanvasTeam")
33
+ if is_running_as_flatpak:
34
+ # Flatpak does not support portable mode
35
+ root_path = Path(os.environ["XDG_DATA_HOME"])
36
+ elif not is_running_portable and is_running_as_compiled:
37
+ root_path = platformdirs.user_data_path("QCanvasReborn", "QCanvasTeam")
41
38
 
42
39
  print("Root path", root_path.absolute())
43
40
  _logger.debug("Root path %s", root_path.absolute())
44
41
  return root_path
45
-
46
-
47
- def ui_storage() -> Path:
48
- return root() / ".UI"
49
-
50
-
51
- def data_storage() -> Path:
52
- return root()
@@ -0,0 +1,20 @@
1
+ import os
2
+ import platform
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ is_running_portable = Path(".portable").exists()
7
+ is_running_on_windows = platform.system() == "Windows"
8
+ is_running_on_linux = platform.system() == "Linux"
9
+ _is_nuitka = "__compiled__" in globals()
10
+ _is_pyinstaller = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
11
+ is_running_as_compiled = _is_nuitka or _is_pyinstaller
12
+ is_running_as_flatpak = os.environ.get("container", "") == "flatpak"
13
+
14
+ __all__ = [
15
+ "is_running_portable",
16
+ "is_running_as_flatpak",
17
+ "is_running_on_linux",
18
+ "is_running_as_compiled",
19
+ "is_running_on_windows",
20
+ ]
@@ -15,6 +15,7 @@ class _ClientSettings:
15
15
  canvas_url: MappedSetting[Optional[str]] = MappedSetting(default=None)
16
16
  canvas_api_key: MappedSetting[Optional[str]] = MappedSetting(default=None)
17
17
  panopto_url: MappedSetting[Optional[str]] = MappedSetting(default=None)
18
+ panopto_disabled = BoolSetting(default=False)
18
19
  quick_sync_enabled = BoolSetting(default=False)
19
20
  sync_on_start = BoolSetting(default=False)
20
21
  download_new_resources = BoolSetting(default=False)
@@ -27,5 +28,13 @@ class _ClientSettings:
27
28
  )
28
29
 
29
30
  @property
30
- def panopto_config(self) -> PanoptoClientConfig:
31
- return PanoptoClientConfig(panopto_url=self.panopto_url)
31
+ def panopto_config(self) -> Optional[PanoptoClientConfig]:
32
+ """
33
+ Generates a panopto client config. If panopto is disabled, it returns None.
34
+ """
35
+
36
+ if self.panopto_disabled:
37
+ _logger.debug("Panopto is disabled")
38
+ return None
39
+ else:
40
+ return PanoptoClientConfig(panopto_url=self.panopto_url)
qcanvas/util/themes.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
 
3
3
  import qdarktheme
4
+ from qtpy.QtWidgets import QApplication, QStyleFactory
4
5
 
5
6
  _logger = logging.getLogger(__name__)
6
7
 
@@ -18,6 +19,8 @@ def apply(theme: str) -> None:
18
19
  theme = ensure_theme_is_valid(theme)
19
20
 
20
21
  if theme != "native":
22
+ QApplication.setStyle(QStyleFactory.create("Fusion"))
23
+
21
24
  qdarktheme.setup_theme(
22
25
  theme,
23
26
  custom_colors={"primary": "e02424"},
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qcanvas
3
- Version: 1.0.12.dev2
3
+ Version: 1.1.0
4
4
  Summary: QCanvas is a desktop client for Canvas LMS.
5
5
  Author: QCanvas
6
6
  Author-email: QCanvas@noreply.codeberg.org
@@ -14,8 +14,8 @@ Requires-Dist: lightdb (>=2.0,<3.0)
14
14
  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
- Requires-Dist: qcanvas-api-clients (>=0.2.2,<0.3.0)
18
- Requires-Dist: qcanvas-backend (==0.1.10)
17
+ Requires-Dist: qcanvas-api-clients (>=0.3.0,<0.4.0)
18
+ Requires-Dist: qcanvas-backend (>=0.2.0,<0.3.0)
19
19
  Requires-Dist: qtpy (>=2.4.1,<3.0.0)
20
20
  Requires-Dist: sqlalchemy (>=2.0.31,<3.0.0)
21
21
  Requires-Dist: validators (>=0.33.0,<0.34.0)