Revert "Add ability to take screenshots in-app and insert them into the page"
This reverts commit 34349c6133.
This commit is contained in:
parent
c2b2eee022
commit
a4d47edba5
8 changed files with 12 additions and 204 deletions
|
|
@ -4,7 +4,6 @@
|
||||||
* Allow clicking on a date in the Statistics heatmap and have it open that 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 the ability to choose the database path at startup
|
||||||
* Add in-app bug report functionality
|
* Add in-app bug report functionality
|
||||||
* Add ability to take screenshots in-app and insert them into the page
|
|
||||||
|
|
||||||
# 0.3.1
|
# 0.3.1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ report from within the app.
|
||||||
* All changes are version controlled, with ability to view/diff versions and revert
|
* All changes are version controlled, with ability to view/diff versions and revert
|
||||||
* Text is Markdown with basic styling
|
* Text is Markdown with basic styling
|
||||||
* Tabs are supported - right-click on a date from the calendar to open it in a new tab.
|
* Tabs are supported - right-click on a date from the calendar to open it in a new tab.
|
||||||
* Images are supported, as is the ability to take a screenshot and have it insert into the page automatically.
|
* Images are supported
|
||||||
* Search all pages, or find text on page (Ctrl+F)
|
* 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
|
* Add tags to pages, find pages by tag in the Tag Browser, and customise tag names and colours
|
||||||
* Automatic periodic saving (or explicitly save)
|
* Automatic periodic saving (or explicitly save)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import importlib.metadata
|
import importlib.metadata
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
|
|
@ -30,6 +32,8 @@ class BugReportDialog(QDialog):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setWindowTitle(strings._("report_a_bug"))
|
self.setWindowTitle(strings._("report_a_bug"))
|
||||||
|
|
||||||
|
self._attachment_path: Path | None = None
|
||||||
|
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
header = QLabel(strings._("bug_report_explanation"))
|
header = QLabel(strings._("bug_report_explanation"))
|
||||||
|
|
@ -44,7 +48,9 @@ class BugReportDialog(QDialog):
|
||||||
|
|
||||||
# Buttons: Cancel / Send
|
# Buttons: Cancel / Send
|
||||||
button_box = QDialogButtonBox(QDialogButtonBox.Cancel)
|
button_box = QDialogButtonBox(QDialogButtonBox.Cancel)
|
||||||
button_box.addButton(strings._("send"), QDialogButtonBox.AcceptRole)
|
self.send_button = button_box.addButton(
|
||||||
|
strings._("send"), QDialogButtonBox.AcceptRole
|
||||||
|
)
|
||||||
button_box.accepted.connect(self._send)
|
button_box.accepted.connect(self._send)
|
||||||
button_box.rejected.connect(self.reject)
|
button_box.rejected.connect(self.reject)
|
||||||
layout.addWidget(button_box)
|
layout.addWidget(button_box)
|
||||||
|
|
|
||||||
|
|
@ -154,9 +154,5 @@
|
||||||
"bug_report_empty": "Please enter some details about the bug before sending.",
|
"bug_report_empty": "Please enter some details about the bug before sending.",
|
||||||
"bug_report_send_failed": "Could not send bug report.",
|
"bug_report_send_failed": "Could not send bug report.",
|
||||||
"bug_report_sent_ok": "Bug report sent. Thank you!",
|
"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"
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -978,7 +978,6 @@ class MainWindow(QMainWindow):
|
||||||
|
|
||||||
tb.historyRequested.connect(self._open_history)
|
tb.historyRequested.connect(self._open_history)
|
||||||
tb.insertImageRequested.connect(self._on_insert_image)
|
tb.insertImageRequested.connect(self._on_insert_image)
|
||||||
tb.insertScreenshotRequested.connect(self._start_screenshot)
|
|
||||||
|
|
||||||
self._toolbar_bound = True
|
self._toolbar_bound = True
|
||||||
|
|
||||||
|
|
@ -1052,21 +1051,6 @@ class MainWindow(QMainWindow):
|
||||||
for path_str in paths:
|
for path_str in paths:
|
||||||
self.editor.insert_image_from_path(Path(path_str))
|
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 ----------------#
|
# ----------- Tags handler ----------------#
|
||||||
def _update_tag_views_for_date(self, date_iso: str):
|
def _update_tag_views_for_date(self, date_iso: str):
|
||||||
if hasattr(self, "tags"):
|
if hasattr(self, "tags"):
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,9 @@ from PySide6.QtGui import (
|
||||||
QTextImageFormat,
|
QTextImageFormat,
|
||||||
QDesktopServices,
|
QDesktopServices,
|
||||||
)
|
)
|
||||||
from PySide6.QtCore import Qt, QRect, QTimer, QUrl, QStandardPaths
|
from PySide6.QtCore import Qt, QRect, QTimer, QUrl
|
||||||
from PySide6.QtWidgets import QTextEdit
|
from PySide6.QtWidgets import QTextEdit
|
||||||
|
|
||||||
from .screenshot import ScreenshotMarkdownInserter
|
|
||||||
from .theme import ThemeManager
|
from .theme import ThemeManager
|
||||||
from .markdown_highlighter import MarkdownHighlighter
|
from .markdown_highlighter import MarkdownHighlighter
|
||||||
|
|
||||||
|
|
@ -1065,8 +1064,3 @@ class MarkdownEditor(QTextEdit):
|
||||||
cursor = self.textCursor()
|
cursor = self.textCursor()
|
||||||
cursor.insertImage(img_format)
|
cursor.insertImage(img_format)
|
||||||
cursor.insertText("\n") # Add newline after image
|
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()
|
|
||||||
|
|
|
||||||
|
|
@ -1,164 +0,0 @@
|
||||||
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""
|
|
||||||
|
|
||||||
if hasattr(self._editor, "textCursor"):
|
|
||||||
cursor = self._editor.textCursor()
|
|
||||||
cursor.insertText(markdown)
|
|
||||||
self._editor.setTextCursor(cursor)
|
|
||||||
else:
|
|
||||||
self._editor.insertPlainText(markdown)
|
|
||||||
|
|
@ -18,7 +18,6 @@ class ToolBar(QToolBar):
|
||||||
checkboxesRequested = Signal()
|
checkboxesRequested = Signal()
|
||||||
historyRequested = Signal()
|
historyRequested = Signal()
|
||||||
insertImageRequested = Signal()
|
insertImageRequested = Signal()
|
||||||
insertScreenshotRequested = Signal()
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(strings._("toolbar_format"), parent)
|
super().__init__(strings._("toolbar_format"), parent)
|
||||||
|
|
@ -82,21 +81,16 @@ class ToolBar(QToolBar):
|
||||||
self.actNumbers.setToolTip(strings._("toolbar_numbered_list"))
|
self.actNumbers.setToolTip(strings._("toolbar_numbered_list"))
|
||||||
self.actNumbers.setCheckable(True)
|
self.actNumbers.setCheckable(True)
|
||||||
self.actNumbers.triggered.connect(self.numbersRequested)
|
self.actNumbers.triggered.connect(self.numbersRequested)
|
||||||
self.actCheckboxes = QAction("☑", self)
|
self.actCheckboxes = QAction("☐", self)
|
||||||
self.actCheckboxes.setToolTip(strings._("toolbar_toggle_checkboxes"))
|
self.actCheckboxes.setToolTip(strings._("toolbar_toggle_checkboxes"))
|
||||||
self.actCheckboxes.triggered.connect(self.checkboxesRequested)
|
self.actCheckboxes.triggered.connect(self.checkboxesRequested)
|
||||||
|
|
||||||
# Images
|
# Images
|
||||||
self.actInsertImg = QAction("🌄", self)
|
self.actInsertImg = QAction(strings._("images"), self)
|
||||||
self.actInsertImg.setToolTip(strings._("insert_images"))
|
self.actInsertImg.setToolTip(strings._("insert_images"))
|
||||||
self.actInsertImg.setShortcut("Ctrl+Shift+I")
|
self.actInsertImg.setShortcut("Ctrl+Shift+I")
|
||||||
self.actInsertImg.triggered.connect(self.insertImageRequested)
|
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
|
# History button
|
||||||
self.actHistory = QAction(strings._("history"), self)
|
self.actHistory = QAction(strings._("history"), self)
|
||||||
self.actHistory.triggered.connect(self.historyRequested)
|
self.actHistory.triggered.connect(self.historyRequested)
|
||||||
|
|
@ -136,7 +130,6 @@ class ToolBar(QToolBar):
|
||||||
self.actNumbers,
|
self.actNumbers,
|
||||||
self.actCheckboxes,
|
self.actCheckboxes,
|
||||||
self.actInsertImg,
|
self.actInsertImg,
|
||||||
self.actScreenshot,
|
|
||||||
self.actHistory,
|
self.actHistory,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue