diff --git a/CHANGELOG.md b/CHANGELOG.md index 78fde07..4d5a6aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,10 @@ -# 0.3.3 - - * Remove screenshot tool - * Improve width of bug report dialog - * Add Tag relationship visualisation tool - # 0.3.2 * Add weekday letters on left axis of Statistics page * Allow clicking on a date in the Statistics heatmap and have it open that page * Add the ability to choose the database path at startup * Add in-app bug report functionality + * Add ability to take screenshots in-app and insert them into the page # 0.3.1 diff --git a/README.md b/README.md index 2df84cf..5610c12 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ report from within the app. * All changes are version controlled, with ability to view/diff versions and revert * Text is Markdown with basic styling * Tabs are supported - right-click on a date from the calendar to open it in a new tab. - * Images are supported + * Images are supported, as is the ability to take a screenshot and have it insert into the page automatically. * Search all pages, or find text on page (Ctrl+F) * Add tags to pages, find pages by tag in the Tag Browser, and customise tag names and colours * Automatic periodic saving (or explicitly save) diff --git a/bouquin/bug_report_dialog.py b/bouquin/bug_report_dialog.py index fae2821..59c8fcc 100644 --- a/bouquin/bug_report_dialog.py +++ b/bouquin/bug_report_dialog.py @@ -1,7 +1,6 @@ from __future__ import annotations import importlib.metadata - import requests from PySide6.QtWidgets import ( @@ -50,8 +49,6 @@ class BugReportDialog(QDialog): button_box.rejected.connect(self.reject) layout.addWidget(button_box) - self.setMinimumWidth(560) - self.text_edit.setFocus() # ------------Helpers ------------ # diff --git a/bouquin/db.py b/bouquin/db.py index ff744ba..25d7527 100644 --- a/bouquin/db.py +++ b/bouquin/db.py @@ -746,49 +746,6 @@ class DBManager: revisions_by_date, ) - def get_tag_cooccurrences(self): - """ - Compute tag–tag co-occurrence across pages. - - Returns: - tags_by_id: dict[int, TagRow] # id -> (id, name, color) - edges: list[(int, int, int)] # (tag_id1, tag_id2, page_count) - tag_page_counts: dict[int, int] # tag_id -> number of pages it appears on - """ - cur = self.conn.cursor() - - # 1) All tags (reuse existing helper) - all_tags: list[TagRow] = self.list_tags() - tags_by_id: dict[int, TagRow] = {t[0]: t for t in all_tags} - - # 2) How many pages each tag appears on (for node sizing) - rows = cur.execute( - """ - SELECT tag_id, COUNT(DISTINCT page_date) AS c - FROM page_tags - GROUP BY tag_id; - """ - ).fetchall() - tag_page_counts = {r["tag_id"]: r["c"] for r in rows} - - # 3) Co-occurrence of tag pairs on the same page - rows = cur.execute( - """ - SELECT - pt1.tag_id AS tag1, - pt2.tag_id AS tag2, - COUNT(DISTINCT pt1.page_date) AS c - FROM page_tags AS pt1 - JOIN page_tags AS pt2 - ON pt1.page_date = pt2.page_date - AND pt1.tag_id < pt2.tag_id - GROUP BY pt1.tag_id, pt2.tag_id; - """, - ).fetchall() - - edges = [(r["tag1"], r["tag2"], r["c"]) for r in rows] - return tags_by_id, edges, tag_page_counts - def close(self) -> None: if self.conn is not None: self.conn.close() diff --git a/bouquin/locales/en.json b/bouquin/locales/en.json index 19cf85d..de72186 100644 --- a/bouquin/locales/en.json +++ b/bouquin/locales/en.json @@ -135,7 +135,6 @@ "delete_tag": "Delete tag", "delete_tag_confirm": "Are you sure you want to delete the tag '{name}'? This will remove it from all pages.", "tag_already_exists_with_that_name": "A tag already exists with that name", - "tag_graph": "Tag relationship graph", "statistics": "Statistics", "main_window_statistics_accessible_flag": "Stat&istics", "stats_pages_with_content": "Pages with content (current version)", @@ -155,5 +154,9 @@ "bug_report_empty": "Please enter some details about the bug before sending.", "bug_report_send_failed": "Could not send bug report.", "bug_report_sent_ok": "Bug report sent. Thank you!", - "send": "Send" + "send": "Send", + "screenshot": "Take screenshot", + "screenshot_could_not_save": "Could not save screenshot", + "screenshot_get_ready": "You will have five seconds to position your screen before a preview is taken.\n\nYou will be able to click and drag to select a specific region of the preview.", + "screenshot_click_and_drag": "Click and drag to select a region or press enter to accept the whole region" } diff --git a/bouquin/main_window.py b/bouquin/main_window.py index 59581fb..3221809 100644 --- a/bouquin/main_window.py +++ b/bouquin/main_window.py @@ -978,6 +978,7 @@ class MainWindow(QMainWindow): tb.historyRequested.connect(self._open_history) tb.insertImageRequested.connect(self._on_insert_image) + tb.insertScreenshotRequested.connect(self._start_screenshot) self._toolbar_bound = True @@ -1051,6 +1052,21 @@ class MainWindow(QMainWindow): for path_str in paths: self.editor.insert_image_from_path(Path(path_str)) + # ----------- Screenshot handler ------------# + def _start_screenshot(self): + ready = QMessageBox.information( + self, + strings._("screenshot"), + strings._("screenshot_get_ready"), + QMessageBox.Ok, + ) + if ready == QMessageBox.Ok: + QGuiApplication.processEvents() + QTimer.singleShot(5000, self._do_screenshot_capture) + + def _do_screenshot_capture(self): + self.editor.take_screenshot() + # ----------- Tags handler ----------------# def _update_tag_views_for_date(self, date_iso: str): if hasattr(self, "tags"): diff --git a/bouquin/markdown_editor.py b/bouquin/markdown_editor.py index 3a33363..d73200c 100644 --- a/bouquin/markdown_editor.py +++ b/bouquin/markdown_editor.py @@ -16,9 +16,10 @@ from PySide6.QtGui import ( QTextImageFormat, QDesktopServices, ) -from PySide6.QtCore import Qt, QRect, QTimer, QUrl +from PySide6.QtCore import Qt, QRect, QTimer, QUrl, QStandardPaths from PySide6.QtWidgets import QTextEdit +from .screenshot import ScreenshotMarkdownInserter from .theme import ThemeManager from .markdown_highlighter import MarkdownHighlighter @@ -1064,3 +1065,8 @@ class MarkdownEditor(QTextEdit): cursor = self.textCursor() cursor.insertImage(img_format) cursor.insertText("\n") # Add newline after image + + def take_screenshot(self): + images_dir = QStandardPaths.writableLocation(QStandardPaths.PicturesLocation) + self._screenshot_helper = ScreenshotMarkdownInserter(self, images_dir, self) + self._screenshot_helper.capture_and_insert() diff --git a/bouquin/screenshot.py b/bouquin/screenshot.py new file mode 100644 index 0000000..0a71e7f --- /dev/null +++ b/bouquin/screenshot.py @@ -0,0 +1,164 @@ +from __future__ import annotations + +from pathlib import Path + +from PySide6.QtCore import Qt, QRect, QPoint, Signal, QDateTime, QObject +from PySide6.QtGui import QGuiApplication, QPainter, QColor, QCursor, QPixmap +from PySide6.QtWidgets import QWidget, QRubberBand, QMessageBox + +from . import strings + + +class ScreenRegionGrabber(QWidget): + regionCaptured = Signal(QPixmap) + + def __init__(self, screenshot_pixmap: QPixmap, parent=None): + super().__init__(parent) + + self._screen_pixmap = screenshot_pixmap + self._selection_rect = QRect() + self._origin = QPoint() + + self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool) + self.setWindowState(Qt.WindowFullScreen) + self.setAttribute(Qt.WA_TranslucentBackground, True) + self.setCursor(Qt.CrossCursor) + + self._rubber_band = QRubberBand(QRubberBand.Rectangle, self) + + def paintEvent(self, event): + painter = QPainter(self) + painter.drawPixmap(self.rect(), self._screen_pixmap) + + # Dim everything + painter.fillRect(self.rect(), QColor(0, 0, 0, 120)) + + # Punch a clear hole for the selection, if there is one + if self._selection_rect.isValid(): + painter.setCompositionMode(QPainter.CompositionMode_Clear) + painter.fillRect(self._selection_rect, Qt.transparent) + painter.setCompositionMode(QPainter.CompositionMode_SourceOver) + else: + # Placeholder text before first click + painter.setPen(QColor(255, 255, 255, 220)) + painter.drawText( + self.rect(), + Qt.AlignCenter, + strings._("screenshot_click_and_drag"), + ) + + painter.end() + + def mousePressEvent(self, event): + if event.button() != Qt.LeftButton: + return + self._origin = event.pos() + self._selection_rect = QRect(self._origin, self._origin) + self._rubber_band.setGeometry(self._selection_rect) + self._rubber_band.show() + + def mouseMoveEvent(self, event): + if not self._rubber_band.isVisible(): + return + self._selection_rect = QRect(self._origin, event.pos()).normalized() + self._rubber_band.setGeometry(self._selection_rect) + self.update() + + def mouseReleaseEvent(self, event): + if event.button() != Qt.LeftButton: + return + if not self._rubber_band.isVisible(): + return + + self._rubber_band.hide() + rect = self._selection_rect.intersected(self._screen_pixmap.rect()) + if rect.isValid(): + cropped = self._screen_pixmap.copy(rect) + self.regionCaptured.emit(cropped) + self.close() + + def keyPressEvent(self, event): + key = event.key() + + # Enter / Return → accept full screen + if key in (Qt.Key_Return, Qt.Key_Enter): + if self._screen_pixmap is not None and not self._screen_pixmap.isNull(): + self.regionCaptured.emit(self._screen_pixmap) + self.close() + return + + # Esc → cancel (no screenshot) + if key == Qt.Key_Escape: + self.close() + return + + # Fallback to default behaviour + super().keyPressEvent(event) + + +class ScreenshotMarkdownInserter(QObject): + """ + Helper that captures a region of the screen, saves it to `images_dir`, + and inserts a Markdown image reference into the MarkdownEditor. + """ + + def __init__(self, editor, images_dir: Path, parent=None): + super().__init__(parent) + self._editor = editor + self._images_dir = Path(images_dir) + self._grabber: ScreenRegionGrabber | None = None + + def capture_and_insert(self): + """ + Starts the screen-region selection overlay. When the user finishes, + the screenshot is saved and the Markdown is inserted in the editor. + """ + screen = QGuiApplication.screenAt(QCursor.pos()) + if screen is None: + screen = QGuiApplication.primaryScreen() + + pixmap = screen.grabWindow(0) + self._grabber = ScreenRegionGrabber(pixmap) + self._grabber.regionCaptured.connect(self._on_region_captured) + self._grabber.show() + + # ------------------------------------------------------------------ internals + + def _on_region_captured(self, pixmap): + if pixmap is None or pixmap.isNull(): + return + + # Ensure output directory exists + self._images_dir.mkdir(parents=True, exist_ok=True) + + timestamp = QDateTime.currentDateTime().toString("yyyyMMdd_HHmmsszzz") + filename = f"bouquin_screenshot_{timestamp}.png" + full_path = self._images_dir / filename + + if not pixmap.save(str(full_path), "PNG"): + QMessageBox.critical( + self, + strings._("screenshot"), + strings._("screenshot_could_not_save"), + ) + return + + self._insert_markdown_image(full_path) + + def _insert_markdown_image(self, path: Path): + """ + Insert image into the MarkdownEditor. + """ + if hasattr(self._editor, "insert_image_from_path"): + self._editor.insert_image_from_path(path) + return + + rel = path.name + markdown = f"![screenshot]({rel})" + + if hasattr(self._editor, "textCursor"): + cursor = self._editor.textCursor() + cursor.insertText(markdown) + self._editor.setTextCursor(cursor) + else: + self._editor.insertPlainText(markdown) diff --git a/bouquin/tag_browser.py b/bouquin/tag_browser.py index c3163b6..1e21bd1 100644 --- a/bouquin/tag_browser.py +++ b/bouquin/tag_browser.py @@ -14,9 +14,8 @@ from PySide6.QtWidgets import ( ) from .db import DBManager -from .tag_graph_dialog import TagGraphDialog -from . import strings from sqlcipher3.dbapi2 import IntegrityError +from . import strings class TagBrowserDialog(QDialog): @@ -72,10 +71,6 @@ class TagBrowserDialog(QDialog): self.delete_btn.setEnabled(False) btn_row.addWidget(self.delete_btn) - self.tag_graph_btn = QPushButton(strings._("tag_graph")) - self.tag_graph_btn.clicked.connect(self._open_tag_graph) - btn_row.addWidget(self.tag_graph_btn) - btn_row.addStretch(1) layout.addLayout(btn_row) @@ -256,9 +251,3 @@ class TagBrowserDialog(QDialog): self._db.delete_tag(tag_id) self._populate(None) self.tagsModified.emit() - - # ------------ Tag graph handler --------------- # - def _open_tag_graph(self): - dlg = TagGraphDialog(self._db, self) - dlg.resize(800, 600) - dlg.exec() diff --git a/bouquin/tag_graph_dialog.py b/bouquin/tag_graph_dialog.py deleted file mode 100644 index ef2d0cc..0000000 --- a/bouquin/tag_graph_dialog.py +++ /dev/null @@ -1,309 +0,0 @@ -import networkx as nx -import numpy as np -import pyqtgraph as pg -from pyqtgraph.Qt import QtCore - -from PySide6.QtWidgets import QDialog, QVBoxLayout, QToolTip -from PySide6.QtGui import QFont, QCursor, QColor - -from .db import DBManager -from . import strings - - -class DraggableGraphItem(pg.GraphItem): - """GraphItem where individual nodes can be dragged with the left mouse button, - and hover events can be reported back to the owning dialog. - """ - - def __init__(self, on_position_changed=None, on_hover=None, **kwds): - # Our own fields MUST be set before super().__init__ because - # GraphItem.__init__ will call self.setData(...) - self._drag_index = None - self._drag_offset = None - self._on_position_changed = on_position_changed - self._on_hover = on_hover - self.pos = None - self._data_kwargs = {} # cache of last setData kwargs - - super().__init__(**kwds) - self.setAcceptHoverEvents(True) - - def setData(self, **kwds): - """Cache kwargs so we don't lose size/adj/brush on drag.""" - if "pos" in kwds: - self.pos = kwds["pos"] - self._data_kwargs.update(kwds) - super().setData(**self._data_kwargs) - - def mouseDragEvent(self, ev): - # --- start of drag --- - if ev.isStart(): - if ev.button() != QtCore.Qt.MouseButton.LeftButton: - ev.ignore() - return - - pos = ev.buttonDownPos() - pts = self.scatter.pointsAt(pos) - - # pointsAt may return an empty list/array - if pts is None or len(pts) == 0: - ev.ignore() - return - - spot = pts[0] - self._drag_index = spot.index() - - node_pos = np.array(self.pos[self._drag_index], dtype=float) - - if hasattr(pos, "x"): - mouse = np.array([pos.x(), pos.y()], dtype=float) - else: - mouse = np.array(pos, dtype=float) - - self._drag_offset = node_pos - mouse - ev.accept() - return - - # --- end of drag --- - if ev.isFinish(): - self._drag_index = None - self._drag_offset = None - ev.accept() - return - - # --- drag in progress --- - if self._drag_index is None: - ev.ignore() - return - - pos = ev.pos() - if hasattr(pos, "x"): - mouse = np.array([pos.x(), pos.y()], dtype=float) - else: - mouse = np.array(pos, dtype=float) - - new_pos = mouse + self._drag_offset - self.pos[self._drag_index] = new_pos # mutate in-place - - # Repaint graph, preserving all the other kwargs (size, adj, colours, ...) - self.setData(pos=self.pos) - - if self._on_position_changed is not None: - self._on_position_changed(self.pos) - - ev.accept() - - def hoverEvent(self, ev): - """Report which node (if any) is under the mouse while hovering.""" - # Leaving the item entirely - if ev.isExit(): - if self._on_hover is not None: - self._on_hover(None, ev) - return - - pos = ev.pos() - pts = self.scatter.pointsAt(pos) - - if pts is None or len(pts) == 0: - if self._on_hover is not None: - self._on_hover(None, ev) - return - - idx = pts[0].index() - if self._on_hover is not None: - self._on_hover(idx, ev) - - -class TagGraphDialog(QDialog): - def __init__(self, db: DBManager, parent=None): - super().__init__(parent) - self.setWindowTitle(strings._("tag_graph")) - - layout = QVBoxLayout(self) - self.view = pg.GraphicsLayoutWidget() - layout.addWidget(self.view) - - self.plot = self.view.addPlot() - self.plot.hideAxis("bottom") - self.plot.hideAxis("left") - - # Dark-ish background, Grafana / neon style - self.view.setBackground("#050816") - self.plot.setMouseEnabled(x=True, y=True) - self.plot.getViewBox().setDefaultPadding(0.15) - - # State for tags / edges / labels / halo - self._label_items = [] - self._tag_ids = [] - self._tag_names = {} - self._tag_page_counts = {} - - self._halo_sizes = [] - self._halo_brushes = [] - - self.graph_item = DraggableGraphItem( - on_position_changed=self._on_positions_changed, - on_hover=self._on_hover_index, - ) - self.plot.addItem(self.graph_item) - - # Separate scatter for "halo" glow behind nodes - self._halo_item = pg.ScatterPlotItem(pxMode=True) - self._halo_item.setZValue(-1) # draw behind nodes/labels - self.plot.addItem(self._halo_item) - - self._populate_graph(db) - - def _populate_graph(self, db: DBManager): - tags_by_id, edges, tag_page_counts = db.get_tag_cooccurrences() - - if not tags_by_id: - return - - # Map tag_id -> index - tag_ids = list(tags_by_id.keys()) - self._tag_ids = tag_ids - self._tag_page_counts = dict(tag_page_counts) - self._tag_names = {tid: tags_by_id[tid][1] for tid in tag_ids} - - idx_of = {tid: i for i, tid in enumerate(tag_ids)} - N = len(tag_ids) - - # ---- Layout: prefer a weighted spring layout via networkx (topic islands) - if edges: - G = nx.Graph() - for tid in tag_ids: - G.add_node(tid) - for t1, t2, w in edges: - G.add_edge(t1, t2, weight=w) - - pos_dict = nx.spring_layout(G, weight="weight", k=1.2, iterations=80) - pos = np.array([pos_dict[tid] for tid in tag_ids], dtype=float) - else: - # Fallback: random-ish blob - pos = np.random.normal(size=(N, 2)) - - # Adjacency (edges) - adj = np.array([[idx_of[t1], idx_of[t2]] for t1, t2, _ in edges], dtype=int) - - # Node sizes: proportional to how often tag is used - max_pages = max(tag_page_counts.values() or [1]) - sizes = np.array( - [10 + 20 * (tag_page_counts.get(tid, 0) / max_pages) for tid in tag_ids], - dtype=float, - ) - - # ---- Neon-style nodes ---- - # Inner fill: dark; outline: tag hex colour - node_brushes = [] - node_pens = [] - - dark_fill = (5, 8, 22, 230) # almost background, slightly lighter - - # For halo - halo_sizes = [] - halo_brushes = [] - - for i, tid in enumerate(tag_ids): - _id, name, color = tags_by_id[tid] - - # node interior (dark) + bright outline - node_brushes.append(pg.mkBrush(dark_fill)) - node_pens.append(pg.mkPen(color, width=2.5)) - - # halo: semi-transparent version of DB colour, larger than node - qcol = QColor(color) - qcol.setAlpha(90) - halo_brushes.append(pg.mkBrush(qcol)) - halo_sizes.append(sizes[i] * 1.8) - - self._halo_sizes = halo_sizes - self._halo_brushes = halo_brushes - - # ---- Edges: softer neon-ish lines with opacity / width based on co-occurrence ---- - if edges: - weights = np.array([w for _, _, w in edges], dtype=float) - max_w = weights.max() if weights.size else 1.0 - weight_factors = (weights / max_w).clip(0.0, 1.0) - - # bright cyan-ish neon - base_color = (56, 189, 248) # tailwind-ish cyan-400 - edge_pens = [] - - for wf in weight_factors: - alpha = int(40 + 160 * wf) # 40–200 - width = 0.7 + 2.3 * wf # 0.7–3.0 - edge_pens.append(pg.mkPen((*base_color, alpha), width=width)) - else: - edge_pens = None - - # Assign data to GraphItem (this will set self.graph_item.pos) - self.graph_item.setData( - pos=pos, - adj=adj, - size=sizes, - symbolBrush=node_brushes, - symbolPen=node_pens, - edgePen=edge_pens, - pxMode=True, - ) - - # ---- Neon halo layer (behind nodes) ---- - xs = [p[0] for p in pos] - ys = [p[1] for p in pos] - self._halo_item.setData( - x=xs, - y=ys, - size=self._halo_sizes, - brush=self._halo_brushes, - pen=None, - ) - - # ---- Add text labels for each tag ---- - self._label_items = [] # reset - font = QFont() - font.setPointSize(8) - - for i, tid in enumerate(tag_ids): - _id, name, color = tags_by_id[tid] - label = pg.TextItem(text=name, color=color, anchor=(0.5, 0.5)) - label.setFont(font) - self.plot.addItem(label) - self._label_items.append(label) - - # Initial placement of labels - self._on_positions_changed(pos) - - def _on_positions_changed(self, pos): - """Called by DraggableGraphItem whenever node positions change.""" - if not self._label_items: - return - - # Update labels - for i, label in enumerate(self._label_items): - label.setPos(float(pos[i, 0]), float(pos[i, 1]) + 0.30) - - # Update halo positions to match nodes - if self._halo_sizes and self._halo_brushes: - xs = [p[0] for p in pos] - ys = [p[1] for p in pos] - self._halo_item.setData( - x=xs, - y=ys, - size=self._halo_sizes, - brush=self._halo_brushes, - pen=None, - ) - - def _on_hover_index(self, index, ev): - """Show ': N pages' when hovering a node.""" - if index is None or not self._tag_ids: - QToolTip.hideText() - return - - tag_id = self._tag_ids[index] - name = self._tag_names.get(tag_id, "") - count = self._tag_page_counts.get(tag_id, 0) - text = f"{name}: {count} page{'s' if count != 1 else ''}" - - QToolTip.showText(QCursor.pos(), text, self) diff --git a/bouquin/toolbar.py b/bouquin/toolbar.py index 89999b8..bcc59de 100644 --- a/bouquin/toolbar.py +++ b/bouquin/toolbar.py @@ -18,6 +18,7 @@ class ToolBar(QToolBar): checkboxesRequested = Signal() historyRequested = Signal() insertImageRequested = Signal() + insertScreenshotRequested = Signal() def __init__(self, parent=None): super().__init__(strings._("toolbar_format"), parent) @@ -81,16 +82,21 @@ class ToolBar(QToolBar): self.actNumbers.setToolTip(strings._("toolbar_numbered_list")) self.actNumbers.setCheckable(True) self.actNumbers.triggered.connect(self.numbersRequested) - self.actCheckboxes = QAction("☐", self) + self.actCheckboxes = QAction("☑", self) self.actCheckboxes.setToolTip(strings._("toolbar_toggle_checkboxes")) self.actCheckboxes.triggered.connect(self.checkboxesRequested) # Images - self.actInsertImg = QAction(strings._("images"), self) + self.actInsertImg = QAction("🌄", self) self.actInsertImg.setToolTip(strings._("insert_images")) self.actInsertImg.setShortcut("Ctrl+Shift+I") self.actInsertImg.triggered.connect(self.insertImageRequested) + self.actScreenshot = QAction("📸", self) + self.actScreenshot.setToolTip(strings._("screenshot")) + self.actScreenshot.setShortcut("Ctrl+Shift+O") + self.actScreenshot.triggered.connect(self.insertScreenshotRequested) + # History button self.actHistory = QAction(strings._("history"), self) self.actHistory.triggered.connect(self.historyRequested) @@ -130,6 +136,7 @@ class ToolBar(QToolBar): self.actNumbers, self.actCheckboxes, self.actInsertImg, + self.actScreenshot, self.actHistory, ] ) diff --git a/poetry.lock b/poetry.lock index 4fa328d..5f6ec39 100644 --- a/poetry.lock +++ b/poetry.lock @@ -257,6 +257,9 @@ files = [ {file = "coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239"}, ] +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + [package.extras] toml = ["tomli"] @@ -274,6 +277,23 @@ files = [ [package.extras] test = ["pyfakefs", "pytest", "pytest-cov"] +[[package]] +name = "exceptiongroup" +version = "1.3.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, + {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "idna" version = "3.11" @@ -299,90 +319,6 @@ files = [ {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] -[[package]] -name = "networkx" -version = "3.5" -description = "Python package for creating and manipulating graphs and networks" -optional = false -python-versions = ">=3.11" -files = [ - {file = "networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec"}, - {file = "networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037"}, -] - -[package.extras] -default = ["matplotlib (>=3.8)", "numpy (>=1.25)", "pandas (>=2.0)", "scipy (>=1.11.2)"] -developer = ["mypy (>=1.15)", "pre-commit (>=4.1)"] -doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=10)", "pydata-sphinx-theme (>=0.16)", "sphinx (>=8.0)", "sphinx-gallery (>=0.18)", "texext (>=0.6.7)"] -example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=2.0.0)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] -extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] -test = ["pytest (>=7.2)", "pytest-cov (>=4.0)", "pytest-xdist (>=3.0)"] -test-extras = ["pytest-mpl", "pytest-randomly"] - -[[package]] -name = "numpy" -version = "2.2.6" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.10" -files = [ - {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf"}, - {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83"}, - {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915"}, - {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680"}, - {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289"}, - {file = "numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d"}, - {file = "numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491"}, - {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a"}, - {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf"}, - {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1"}, - {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab"}, - {file = "numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47"}, - {file = "numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282"}, - {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87"}, - {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249"}, - {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49"}, - {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de"}, - {file = "numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4"}, - {file = "numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566"}, - {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f"}, - {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f"}, - {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868"}, - {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d"}, - {file = "numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd"}, - {file = "numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8"}, - {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f"}, - {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa"}, - {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571"}, - {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1"}, - {file = "numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff"}, - {file = "numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00"}, - {file = "numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd"}, -] - [[package]] name = "packaging" version = "25.0" @@ -437,20 +373,7 @@ files = [ [package.dependencies] desktop-entry-lib = "*" requests = "*" - -[[package]] -name = "pyqtgraph" -version = "0.14.0" -description = "Scientific Graphics and GUI Library for Python" -optional = false -python-versions = ">=3.10" -files = [ - {file = "pyqtgraph-0.14.0-py3-none-any.whl", hash = "sha256:7abb7c3e17362add64f8711b474dffac5e7b0e9245abdf992e9a44119b7aa4f5"}, -] - -[package.dependencies] -colorama = "*" -numpy = ">=1.25.0" +tomli = {version = "*", markers = "python_version < \"3.11\""} [[package]] name = "pyside6" @@ -519,10 +442,12 @@ files = [ [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} iniconfig = ">=1" packaging = ">=20" pluggy = ">=1.5,<2" pygments = ">=2.7.2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] @@ -749,6 +674,57 @@ files = [ {file = "sqlcipher3_wheels-0.5.5.post0.tar.gz", hash = "sha256:2c291ba05fa3e57c9b4d407d2751aa69266b5372468e7402daaa312b251aca7f"}, ] +[[package]] +name = "tomli" +version = "2.3.0" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c"}, + {file = "tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456"}, + {file = "tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6"}, + {file = "tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876"}, + {file = "tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05"}, + {file = "tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606"}, + {file = "tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005"}, + {file = "tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463"}, + {file = "tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f"}, + {file = "tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0"}, + {file = "tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba"}, + {file = "tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b"}, + {file = "tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549"}, +] + [[package]] name = "typing-extensions" version = "4.15.0" @@ -779,5 +755,5 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" -python-versions = ">=3.11,<3.14" -content-hash = "02e508f6d5fc3843084d48a8e495af69555ce6163e281d62b2d3242378e68274" +python-versions = ">=3.9,<3.14" +content-hash = "cdb45b4ed472b5fd77e4c5b6ccd88ad4402b4e5473279627177cccb960a29f27" diff --git a/pyproject.toml b/pyproject.toml index cb85a11..631d122 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,12 +10,10 @@ packages = [{ include = "bouquin" }] include = ["bouquin/locales/*.json"] [tool.poetry.dependencies] -python = ">=3.11,<3.14" +python = ">=3.9,<3.14" pyside6 = ">=6.8.1,<7.0.0" sqlcipher3-wheels = "^0.5.5.post0" requests = "^2.32.5" -pyqtgraph = "^0.14.0" -networkx = "^3.5" [tool.poetry.scripts] bouquin = "bouquin.__main__:main" diff --git a/vulture_ignorelist.py b/vulture_ignorelist.py index b1ec549..c4ea333 100644 --- a/vulture_ignorelist.py +++ b/vulture_ignorelist.py @@ -3,15 +3,11 @@ from bouquin.flow_layout import FlowLayout from bouquin.markdown_editor import MarkdownEditor from bouquin.markdown_highlighter import MarkdownHighlighter from bouquin.statistics_dialog import DateHeatMap -from bouquin.tag_graph_dialog import DraggableGraphItem DBManager.row_factory DateHeatMap.minimumSizeHint -DraggableGraphItem.hoverEvent -DraggableGraphItem.mouseDragEvent - FlowLayout.itemAt FlowLayout.expandingDirections FlowLayout.hasHeightForWidth