oqtopus 0.2.1__py3-none-any.whl → 1.0.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.
@@ -17,10 +17,11 @@
17
17
  ***************************************************************************/
18
18
  """
19
19
 
20
+ import glob
20
21
  import logging
21
22
  import os
23
+ from datetime import datetime
22
24
  from logging import LogRecord
23
- from logging.handlers import TimedRotatingFileHandler
24
25
 
25
26
  from qgis.PyQt.QtCore import (
26
27
  QDir,
@@ -34,12 +35,14 @@ from qgis.PyQt.QtCore import (
34
35
  from qgis.PyQt.QtGui import QColor, QDesktopServices, QIcon
35
36
  from qgis.PyQt.uic import loadUiType
36
37
 
38
+ from ..libs.pum import SQL
39
+
37
40
  logger = logging.getLogger("oqtopus")
38
41
 
39
42
 
40
43
  class PluginUtils:
41
44
 
42
- PLUGIN_NAME = "Oqtopus"
45
+ PLUGIN_NAME = "oQtopus"
43
46
 
44
47
  logsDirectory = ""
45
48
 
@@ -55,8 +58,30 @@ class PluginUtils:
55
58
  """
56
59
  return os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
57
60
 
61
+ @staticmethod
62
+ def plugin_cache_path():
63
+ """Returns the persistent cache path for the plugin (GitHub API cache, downloaded packages).
64
+
65
+ Uses CacheLocation which is appropriate for data that should persist
66
+ across sessions but can be safely deleted.
67
+ On macOS: ~/Library/Caches/oqtopus
68
+ On Linux: ~/.cache/oqtopus
69
+ On Windows: C:/Users/<USER>/AppData/Local/oqtopus/cache
70
+ """
71
+ cache_dir = os.path.join(
72
+ QStandardPaths.writableLocation(QStandardPaths.StandardLocation.CacheLocation),
73
+ "oqtopus",
74
+ )
75
+ os.makedirs(cache_dir, exist_ok=True)
76
+ return cache_dir
77
+
58
78
  @staticmethod
59
79
  def plugin_temp_path():
80
+ """Returns the temporary path for the plugin.
81
+
82
+ DEPRECATED: Use plugin_cache_path() instead for persistent cache.
83
+ This is kept for backward compatibility.
84
+ """
60
85
  plugin_basename = PluginUtils.plugin_root_path().split(os.sep)[-1]
61
86
 
62
87
  plugin_temp_dir = os.path.join(
@@ -68,6 +93,33 @@ class PluginUtils:
68
93
 
69
94
  return plugin_temp_dir
70
95
 
96
+ @staticmethod
97
+ def get_all_cache_paths():
98
+ """Returns all cache directories used by the plugin.
99
+
100
+ This is used by the cleanup function to ensure all caches are deleted.
101
+ """
102
+ paths = []
103
+
104
+ # Main cache directory (CacheLocation)
105
+ cache_path = os.path.join(
106
+ QStandardPaths.writableLocation(QStandardPaths.StandardLocation.CacheLocation),
107
+ "oqtopus",
108
+ )
109
+ if os.path.exists(cache_path):
110
+ paths.append(cache_path)
111
+
112
+ # Legacy temp directory (TempLocation) - for backward compatibility
113
+ plugin_basename = PluginUtils.plugin_root_path().split(os.sep)[-1]
114
+ temp_path = os.path.join(
115
+ QStandardPaths.writableLocation(QStandardPaths.StandardLocation.TempLocation),
116
+ plugin_basename,
117
+ )
118
+ if os.path.exists(temp_path):
119
+ paths.append(temp_path)
120
+
121
+ return paths
122
+
71
123
  @staticmethod
72
124
  def get_plugin_icon_path(icon_filename):
73
125
  return os.path.join(PluginUtils.plugin_root_path(), "icons", icon_filename)
@@ -108,33 +160,66 @@ class PluginUtils:
108
160
  directory.mkpath(PluginUtils.logsDirectory)
109
161
 
110
162
  if directory.exists():
111
- logfile = QFileInfo(directory, "Oqtopus.log")
112
-
113
- # Handler for files rotation, create one log per day
114
- rotationHandler = TimedRotatingFileHandler(
115
- logfile.filePath(), when="midnight", backupCount=10
116
- )
117
-
118
- # Configure logging
119
- logging.basicConfig(
120
- level=logging.DEBUG,
121
- format="%(asctime)s %(levelname)-7s %(message)s",
122
- handlers=[rotationHandler],
123
- )
163
+ # Create a new log file for each session with timestamp
164
+ timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
165
+ logfile = QFileInfo(directory, f"oQtopus_{timestamp}.log")
166
+ PluginUtils.currentLogFile = logfile.filePath()
167
+
168
+ # Clean up old log files, keep only the 10 most recent
169
+ PluginUtils._cleanup_old_logs(PluginUtils.logsDirectory, keep=10)
170
+
171
+ # Create file handler for this session
172
+ fileHandler = logging.FileHandler(logfile.filePath(), mode="w")
173
+ fileHandler.setLevel(SQL) # Use SQL level (5) to capture all messages including SQL
174
+ fileHandler.setFormatter(logging.Formatter("%(asctime)s %(levelname)-7s %(message)s"))
175
+
176
+ # Configure logging - basicConfig might not work if already called
177
+ # so we configure the root logger directly
178
+ root_logger = logging.getLogger()
179
+ root_logger.setLevel(SQL) # Set root to SQL level to allow SQL messages through
180
+ root_logger.addHandler(fileHandler)
181
+
182
+ # Set the pum library to SQL level (5) to capture all SQL statements
183
+ # SQL (5) < DEBUG (10) < INFO (20)
184
+ logging.getLogger("pum").setLevel(SQL)
124
185
  else:
125
186
  logger.error(f"Can't create log files directory '{PluginUtils.logsDirectory}'.")
126
187
 
188
+ @staticmethod
189
+ def _cleanup_old_logs(logs_dir: str, keep: int = 10):
190
+ """Remove old log files, keeping only the most recent ones.
191
+
192
+ Args:
193
+ logs_dir: Directory containing log files
194
+ keep: Number of most recent log files to keep
195
+ """
196
+ log_files = glob.glob(os.path.join(logs_dir, "oQtopus_*.log"))
197
+ # Sort by modification time, newest first
198
+ log_files.sort(key=os.path.getmtime, reverse=True)
199
+ # Remove old files beyond the keep limit
200
+ for old_file in log_files[keep:]:
201
+ try:
202
+ os.remove(old_file)
203
+ except OSError:
204
+ pass # Ignore errors when removing old logs
205
+
127
206
  @staticmethod
128
207
  def open_logs_folder():
129
208
  QDesktopServices.openUrl(QUrl.fromLocalFile(PluginUtils.logsDirectory))
130
209
 
131
210
  @staticmethod
132
211
  def open_log_file():
133
- log_file_path = os.path.join(PluginUtils.logsDirectory, "Oqtopus.log")
134
- if os.path.exists(log_file_path):
212
+ log_file_path = getattr(PluginUtils, "currentLogFile", None)
213
+ if log_file_path and os.path.exists(log_file_path):
135
214
  QDesktopServices.openUrl(QUrl.fromLocalFile(log_file_path))
136
215
  else:
137
- logger.error(f"Log file '{log_file_path}' does not exist.")
216
+ # Fallback: open most recent log file
217
+ log_files = glob.glob(os.path.join(PluginUtils.logsDirectory, "oQtopus_*.log"))
218
+ if log_files:
219
+ log_files.sort(key=os.path.getmtime, reverse=True)
220
+ QDesktopServices.openUrl(QUrl.fromLocalFile(log_files[0]))
221
+ else:
222
+ logger.error("No log file found.")
138
223
 
139
224
  @staticmethod
140
225
  def get_github_token():
@@ -146,6 +231,16 @@ class PluginUtils:
146
231
  settings = QSettings()
147
232
  settings.setValue("oqtopus/github_token", token)
148
233
 
234
+ @staticmethod
235
+ def get_allow_multiple_modules():
236
+ settings = QSettings()
237
+ return settings.value("oqtopus/allow_multiple_modules", False, type=bool)
238
+
239
+ @staticmethod
240
+ def set_allow_multiple_modules(allow: bool):
241
+ settings = QSettings()
242
+ settings.setValue("oqtopus/allow_multiple_modules", allow)
243
+
149
244
  @staticmethod
150
245
  def get_github_headers():
151
246
  token = PluginUtils.get_github_token()
@@ -176,5 +271,4 @@ class LoggingBridge(logging.Handler, QObject):
176
271
 
177
272
  def emit(self, record):
178
273
  log_entry = self.format(record)
179
- print(log_entry)
180
274
  self.loggedLine.emit(record, log_entry)
oqtopus/utils/qt_utils.py CHANGED
@@ -68,6 +68,60 @@ class QtUtils:
68
68
  font.setItalic(italic)
69
69
  widget.setFont(font)
70
70
 
71
+ @staticmethod
72
+ def setTextWithEllipsis(label, text, max_length=80):
73
+ """
74
+ Set text on a label with ellipsis in the middle if too long.
75
+ Sets full text as tooltip.
76
+ :param label: The QLabel widget to set text on.
77
+ :param text: The text to display.
78
+ :param max_length: Maximum length before truncating (default: 80).
79
+ """
80
+ if len(text) <= max_length:
81
+ label.setText(text)
82
+ label.setToolTip("")
83
+ else:
84
+ # Calculate how many characters to keep on each side
85
+ side_length = (max_length - 3) // 2 # 3 for the ellipsis
86
+ truncated = f"{text[:side_length]}…{text[-side_length:]}"
87
+ label.setText(truncated)
88
+ label.setToolTip(text)
89
+
90
+ @staticmethod
91
+ def shortenPath(path: str, max_length: int = 50) -> str:
92
+ """Shorten a long path by replacing middle part with ellipsis.
93
+
94
+ Args:
95
+ path: The full path to shorten
96
+ max_length: Maximum length before shortening
97
+
98
+ Returns:
99
+ Shortened path with … in the middle if too long
100
+ """
101
+ if len(path) <= max_length:
102
+ return path
103
+
104
+ # Calculate how many characters to keep from start and end
105
+ # Reserve 1 character for the ellipsis
106
+ chars_to_keep = max_length - 1
107
+ start_chars = chars_to_keep // 2
108
+ end_chars = chars_to_keep - start_chars
109
+
110
+ return f"{path[:start_chars]}…{path[-end_chars:]}"
111
+
112
+ @staticmethod
113
+ def setPathLinkWithEllipsis(label, path: str, max_length: int = 50):
114
+ """Set a file path as a clickable link on a label, with ellipsis for long paths.
115
+
116
+ Args:
117
+ label: The QLabel widget to set the link on.
118
+ path: The full file path.
119
+ max_length: Maximum display length before truncating (default: 50).
120
+ """
121
+ display_path = QtUtils.shortenPath(str(path), max_length)
122
+ label.setText(f"<a href='file://{path}'>{display_path}</a>")
123
+ label.setToolTip(str(path))
124
+
71
125
 
72
126
  class CriticalMessageBox(QMessageBox):
73
127
  def __init__(self, title: str, description: str, exception: Exception = None, parent=None):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oqtopus
3
- Version: 0.2.1
3
+ Version: 1.0.0
4
4
  Summary: oQtopus : A QGIS module manager that helps you deploy, manage and upgrade your QGIS projects, plugins and associated postgreSQL / PostGIS data model implementations
5
5
  Author-email: Damiano Lombardi <damiano@opengis.ch>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -0,0 +1,47 @@
1
+ oqtopus/__init__.py,sha256=PUl5ObSgoyF6tcNphBip9Rff8lul0t9VobiwsLKwgFA,104
2
+ oqtopus/oqtopus.py,sha256=BF_JSF2oOCzc8Qr7Z0B7FJdYrdAR3FR9L7oXh3QatL4,1860
3
+ oqtopus/oqtopus_plugin.py,sha256=G5gDxJQQuTwhaf7b-sNDvcd_W4S1BQ1WC1msXJLmqKw,6516
4
+ oqtopus/core/module.py,sha256=iQ4UqI-HJ1X5I1uVEvs7Kplrt9lWxYun4r1FO7kiKmw,10060
5
+ oqtopus/core/module_asset.py,sha256=qr_wwriJ_rZd86EuBo_Rx-MDCDJNYA6odhk6F-Z6YFA,467
6
+ oqtopus/core/module_operation_task.py,sha256=YCBrIsbmJvS4WpXlRw4XioZWZgSaPl8RTBxv5qgMmSQ,8352
7
+ oqtopus/core/module_package.py,sha256=b5fEMVDl1r0fqhswFT24UPRebar68hrEfw82MH41yvI,4988
8
+ oqtopus/core/modules_config.py,sha256=-56Jm1s0NsB-Byf2qxEm3vzsgDm2RsRSCtCSJH8s5Xk,276
9
+ oqtopus/core/package_prepare_task.py,sha256=zpUnTIM57wqt_DDk4kvpllrXc3muDqKd94lB7XhjwKY,15387
10
+ oqtopus/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ oqtopus/gui/about_dialog.py,sha256=dDNwHxKf_M82Z-TKmXW1fXmnI_WSV_7MShqz05trzRw,2345
12
+ oqtopus/gui/database_connection_widget.py,sha256=swsHmIXdYICTDHeoROGocVqhAVmTysszzlgnfwWIUbo,7044
13
+ oqtopus/gui/database_create_dialog.py,sha256=INyajKxVeIfUS5GWLPmC4nEUAVTck1SxGod99qLDu8I,8901
14
+ oqtopus/gui/database_duplicate_dialog.py,sha256=9byfL6WBYuep4aBU9_YZaRmJ_96wwrXXNTSO-7rzhlM,4969
15
+ oqtopus/gui/logs_widget.py,sha256=mcxlxvH9XHSIwToTHtWiwlaT1-446ZDwtouYc9ZwW20,9795
16
+ oqtopus/gui/main_dialog.py,sha256=smpYnRtx5WuAk-R7Azmu83L7346w_bKPtVCv-J23uxE,10303
17
+ oqtopus/gui/module_selection_widget.py,sha256=ivVLsSo0c7otVa3qET5RFvPgGoICbles0ydfuf-Jmvo,20623
18
+ oqtopus/gui/module_widget.py,sha256=uUUHErSqEkY2Qn3dqb1n29ql2uvlYR2NH-mEKrgUO_g,34483
19
+ oqtopus/gui/parameters_groupbox.py,sha256=ZAAu4b38k9js6EsW9VSZWyZ2ZG_KBDLKMUn2qeLJoV8,3489
20
+ oqtopus/gui/plugin_widget.py,sha256=vIcmaSw_0ZTaalaAwSbXWZNbQ0-hwD2MmpeMIU_-DYg,6382
21
+ oqtopus/gui/project_widget.py,sha256=7noewlFmMwa8tIMgzcHnmRVOY0z9Al0W9lEdYrvmeUg,7455
22
+ oqtopus/gui/settings_dialog.py,sha256=e-n7BatRbR-2pQleteKmEQs0zFfijBt-GYyVI6oDJHA,1742
23
+ oqtopus/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ oqtopus/ui/about_dialog.ui,sha256=tAc1F5NYOsM3rMwYBes1Id0avaLeFo0uSOMXRUK89UM,6244
25
+ oqtopus/ui/database_connection_widget.ui,sha256=0hLBaOx0KcLTH6RlkziVj-tsczjHDUGC-3EXQYgcTuE,3729
26
+ oqtopus/ui/database_create_dialog.ui,sha256=c-fZJRH4tmAeOr6O6M2sPIhzzYQMrSc1DI6a7at-UjI,9230
27
+ oqtopus/ui/database_duplicate_dialog.ui,sha256=EzMRYhlxsiP_sr9ixBJn4lnefM5R_p1oXx4RCyMAj9U,3826
28
+ oqtopus/ui/logs_widget.ui,sha256=oAgEtRUBXB-DzLC0vmBP-PEA-Uu3d3ppFm0ahs1jHQI,2785
29
+ oqtopus/ui/main_dialog.ui,sha256=A1U97A6Cl9h3GHtI4U6ZNI7KXpMEJM6aBKheuAshzto,2650
30
+ oqtopus/ui/module_selection_widget.ui,sha256=bsuOdy19iRuDfRjfJEK4HcNAyr8pT-GIhyAQoi1zyj4,5646
31
+ oqtopus/ui/module_widget.ui,sha256=LczVV5FBnvYPrQW32EZPvuei4PV7glsIC686w1ejkkA,6251
32
+ oqtopus/ui/plugin_widget.ui,sha256=osJ_QwzmXahV5fEHk4WfUjBBQ7jdsrL4pixiMF2jURA,2570
33
+ oqtopus/ui/project_widget.ui,sha256=5YqSjHR8mQFmHzbSB2M6JnNfrpkqhlRC0N9MSlgcXY8,1600
34
+ oqtopus/ui/settings_dialog.ui,sha256=iJ3yg3xWRFYa6DfMVKNv8EgFEb9gKRqAkUu_lgP6xQk,2472
35
+ oqtopus/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
+ oqtopus/utils/plugin_utils.py,sha256=ZOWYfI162suuVPROqJ2z0mbnl5JW3ff1gfbZ_iBr91o,9809
37
+ oqtopus/utils/qt_utils.py,sha256=ByJxFfbml1wwHvRvKxRNvlgltVU6zI1dmOjgALtFXT8,5291
38
+ oqtopus/utils/tmmtlogging.py,sha256=BSAPrhQGCuH2boMG4bP8QFyLfoss1uR4pO_pXjTA1eQ,1733
39
+ oqtopus/utils/translation.py,sha256=p1d5N6CYIf94fGYdPQnJamdum9MVBlPkJ24c557rqdg,2647
40
+ oqtopus-1.0.0.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
41
+ tests/__init__.py,sha256=z5CxS92efenLcDm64Sifx26EjZBCWPO4oVvaEEHJJ6w,444
42
+ tests/test_imports.py,sha256=uxa1OGKq2RVXm1lPQT3qb23hWd9yCncLI9aVWM1Cz_I,2507
43
+ tests/test_plugin_load.py,sha256=HuVLdrsn2PSjKIrvcLj0EH7vNE8-0jBMmHZHihsqB3Q,517
44
+ oqtopus-1.0.0.dist-info/METADATA,sha256=h2x2M-pF49m5CIqPqjX4rF2sFygrZUxJ4yJqKJihxfA,21787
45
+ oqtopus-1.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
46
+ oqtopus-1.0.0.dist-info/top_level.txt,sha256=i4DHi21kcGIzrF8DlgsKj-UCENHg_NebTRac7cwt32A,14
47
+ oqtopus-1.0.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
tests/test_imports.py ADDED
@@ -0,0 +1,59 @@
1
+ """Test to ensure bundled libraries are imported correctly."""
2
+
3
+ import ast
4
+ from pathlib import Path
5
+
6
+
7
+ def test_no_direct_bundled_lib_imports():
8
+ """
9
+ Ensure that pum and pgserviceparser are never imported directly.
10
+ They should always be imported from oqtopus.libs to use the bundled versions.
11
+ """
12
+ oqtopus_dir = Path(__file__).parent.parent / "oqtopus"
13
+ forbidden_imports = ["pum", "pgserviceparser"]
14
+ violations = []
15
+
16
+ # Scan all Python files in the oqtopus directory
17
+ for py_file in oqtopus_dir.rglob("*.py"):
18
+ # Skip __init__.py in libs directory (it's allowed to import directly)
19
+ if py_file.parent.name == "libs" and py_file.name == "__init__.py":
20
+ continue
21
+
22
+ try:
23
+ with open(py_file, encoding="utf-8") as f:
24
+ tree = ast.parse(f.read(), filename=str(py_file))
25
+
26
+ for node in ast.walk(tree):
27
+ # Check for "import pum" or "import pgserviceparser"
28
+ if isinstance(node, ast.Import):
29
+ for alias in node.names:
30
+ if alias.name in forbidden_imports or any(
31
+ alias.name.startswith(f"{lib}.") for lib in forbidden_imports
32
+ ):
33
+ violations.append(
34
+ f"{py_file.relative_to(oqtopus_dir.parent)}: "
35
+ f"Direct import '{alias.name}' found at line {node.lineno}"
36
+ )
37
+
38
+ # Check for "from pum import ..." or "from pgserviceparser import ..."
39
+ elif isinstance(node, ast.ImportFrom):
40
+ if node.module in forbidden_imports or any(
41
+ node.module and node.module.startswith(f"{lib}.")
42
+ for lib in forbidden_imports
43
+ ):
44
+ violations.append(
45
+ f"{py_file.relative_to(oqtopus_dir.parent)}: "
46
+ f"Direct import 'from {node.module}' found at line {node.lineno}"
47
+ )
48
+
49
+ except SyntaxError:
50
+ # Skip files with syntax errors (might be templates or non-Python)
51
+ continue
52
+
53
+ if violations:
54
+ error_msg = (
55
+ "Found direct imports of bundled libraries. "
56
+ "Please use 'from ..libs.pum import ...' or 'from ..libs.pgserviceparser import ...' instead:\n"
57
+ + "\n".join(violations)
58
+ )
59
+ raise AssertionError(error_msg)
@@ -1,45 +0,0 @@
1
- oqtopus/__init__.py,sha256=PUl5ObSgoyF6tcNphBip9Rff8lul0t9VobiwsLKwgFA,104
2
- oqtopus/oqtopus.py,sha256=BF_JSF2oOCzc8Qr7Z0B7FJdYrdAR3FR9L7oXh3QatL4,1860
3
- oqtopus/oqtopus_plugin.py,sha256=WJQrEcH1l08Yhmvk4POv9Coh27q4k5i5-vKOTPwCs3k,6116
4
- oqtopus/core/module.py,sha256=47iDvEOXFNWY77cbi7LhQs5qTUwRNuauieqNTPf9UKY,4766
5
- oqtopus/core/module_asset.py,sha256=qr_wwriJ_rZd86EuBo_Rx-MDCDJNYA6odhk6F-Z6YFA,467
6
- oqtopus/core/module_package.py,sha256=sYCnKMOMW4R9ULP3oresDNMAaFEb8y_pLa4Lvjdl7RI,3958
7
- oqtopus/core/modules_config.py,sha256=iEN_JYuFuGpF4dyfnmcJPfp_bIzrn_PNbq9G1DElyCg,186
8
- oqtopus/core/package_prepare_task.py,sha256=8Oe876v1XDJSJgtO--f087nMRkFSIay14Eb1pjyy-Xg,5759
9
- oqtopus/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- oqtopus/gui/about_dialog.py,sha256=dDNwHxKf_M82Z-TKmXW1fXmnI_WSV_7MShqz05trzRw,2345
11
- oqtopus/gui/database_connection_widget.py,sha256=_SM9K-2dUmXMgjJzqdWrfutRnU4I0WDWZ2FM1dkIa6A,6688
12
- oqtopus/gui/database_create_dialog.py,sha256=Z7XK47EVs3S1GKWEGOZDE5a4cEWUgW4NcPzAD0JqyEg,8880
13
- oqtopus/gui/database_duplicate_dialog.py,sha256=Scc1eo8hmniAIyDDK_Qks1RgZn6MsDbtIokg9woEldA,4944
14
- oqtopus/gui/logs_widget.py,sha256=lxP0rZYUF4ahR7RtM3SJCbk5nk70SVC9Ien6TvBsflY,6698
15
- oqtopus/gui/main_dialog.py,sha256=OX63uivnOuc9hlv1KAxjkh1F7Yxu_NCjlfcg4569BBU,6874
16
- oqtopus/gui/module_selection_widget.py,sha256=kjU2cQmK9lYcuKnZwsbRbqrG6dR43hpQkc9S16TT0vE,16180
17
- oqtopus/gui/module_widget.py,sha256=ckdhXYfUrvOy2mbYGUTYOrDIDplWypmqJ7cp-Pyf1qQ,8529
18
- oqtopus/gui/parameters_groupbox.py,sha256=ZXoIbTDrpoe_SfxyYr7mNbQOi0vHAJSW8cPIXzGKdmE,2938
19
- oqtopus/gui/plugin_widget.py,sha256=8av8KI3p8gnXOlSssB5-dFVvq4vaLfqGjY9NVSNiBb0,5757
20
- oqtopus/gui/project_widget.py,sha256=FhWvMBtQ6kw7NJ4pdCzNs48wMrA5ihjvcbplE4RMWbE,7281
21
- oqtopus/gui/settings_dialog.py,sha256=-iHT86Ltm1Gsk30YmDL4U0C9yK8LFqt15obZOBdKb28,1547
22
- oqtopus/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- oqtopus/ui/about_dialog.ui,sha256=tAc1F5NYOsM3rMwYBes1Id0avaLeFo0uSOMXRUK89UM,6244
24
- oqtopus/ui/database_connection_widget.ui,sha256=0hLBaOx0KcLTH6RlkziVj-tsczjHDUGC-3EXQYgcTuE,3729
25
- oqtopus/ui/database_create_dialog.ui,sha256=c-fZJRH4tmAeOr6O6M2sPIhzzYQMrSc1DI6a7at-UjI,9230
26
- oqtopus/ui/database_duplicate_dialog.ui,sha256=EzMRYhlxsiP_sr9ixBJn4lnefM5R_p1oXx4RCyMAj9U,3826
27
- oqtopus/ui/logs_widget.ui,sha256=oAgEtRUBXB-DzLC0vmBP-PEA-Uu3d3ppFm0ahs1jHQI,2785
28
- oqtopus/ui/main_dialog.ui,sha256=A1U97A6Cl9h3GHtI4U6ZNI7KXpMEJM6aBKheuAshzto,2650
29
- oqtopus/ui/module_selection_widget.ui,sha256=IWfoWT5vHoayQPoOPIHOJlg4JdNmVX8DHFQnTIv-Hdk,5646
30
- oqtopus/ui/module_widget.ui,sha256=2sm0J-qb4CRiKaGk5lyg6AMWlq0d2hE0IW5OSYmJQZo,5662
31
- oqtopus/ui/plugin_widget.ui,sha256=osJ_QwzmXahV5fEHk4WfUjBBQ7jdsrL4pixiMF2jURA,2570
32
- oqtopus/ui/project_widget.ui,sha256=5YqSjHR8mQFmHzbSB2M6JnNfrpkqhlRC0N9MSlgcXY8,1600
33
- oqtopus/ui/settings_dialog.ui,sha256=3PqxTMo22Ola1zl6XkwhXCi6G-uum6g_1-nbjSXWilI,2216
34
- oqtopus/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- oqtopus/utils/plugin_utils.py,sha256=uGuhiiVC0qfNYH5vN-XE8poB22aNZyZeHf2g515uUrI,5950
36
- oqtopus/utils/qt_utils.py,sha256=HPe9tOQQH9R9xZp4rGphVhSJO7220q368xmreDu5-6g,3243
37
- oqtopus/utils/tmmtlogging.py,sha256=BSAPrhQGCuH2boMG4bP8QFyLfoss1uR4pO_pXjTA1eQ,1733
38
- oqtopus/utils/translation.py,sha256=p1d5N6CYIf94fGYdPQnJamdum9MVBlPkJ24c557rqdg,2647
39
- oqtopus-0.2.1.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
40
- tests/__init__.py,sha256=z5CxS92efenLcDm64Sifx26EjZBCWPO4oVvaEEHJJ6w,444
41
- tests/test_plugin_load.py,sha256=HuVLdrsn2PSjKIrvcLj0EH7vNE8-0jBMmHZHihsqB3Q,517
42
- oqtopus-0.2.1.dist-info/METADATA,sha256=J_suhLnR0IIpH4Aws2UIfM9k5pUOCjzH3Ctzqwpky24,21787
43
- oqtopus-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
- oqtopus-0.2.1.dist-info/top_level.txt,sha256=i4DHi21kcGIzrF8DlgsKj-UCENHg_NebTRac7cwt32A,14
45
- oqtopus-0.2.1.dist-info/RECORD,,