Files
tabRemember/app.py

322 lines
12 KiB
Python

import sys
import json
import os
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QLineEdit, QPushButton, QListWidget, QTextEdit, QComboBox,
QMessageBox, QSpacerItem, QSizePolicy, QLabel, QFileDialog, QDialog, QFormLayout, QTableWidget, QTableWidgetItem
)
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtCore import Qt, QUrl, QDateTime
from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtGui import QDesktopServices
class URLManager(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('tabRemember - URL Manager')
self.setGeometry(100, 100, 400, 600)
# Set the application icon
self.setWindowIcon(QIcon('assets/logo.png'))
self.layout = QVBoxLayout()
# URL input and settings button layout
self.url_layout = QHBoxLayout()
self.url_input = QLineEdit()
self.url_input.setPlaceholderText("URL")
self.url_layout.addWidget(self.url_input)
# Settings button
self.settings_button = QPushButton()
self.settings_button.setFixedHeight(self.url_input.sizeHint().height())
self.settings_button.setFixedWidth(self.url_input.sizeHint().height())
self.settings_button.clicked.connect(self.show_settings_dialog)
self.url_layout.addWidget(self.settings_button)
# Info button
self.info_button = QPushButton()
self.info_button.setFixedHeight(self.url_input.sizeHint().height())
self.info_button.setFixedWidth(self.url_input.sizeHint().height())
self.info_button.clicked.connect(self.show_info_dialog)
self.url_layout.addWidget(self.info_button)
self.layout.addLayout(self.url_layout)
# Update icons based on the color mode
self.update_icon_color()
# Description/Thoughts input
self.description_input = QTextEdit()
self.description_input.setPlaceholderText("Enter Description or Thoughts")
self.description_input.setMaximumHeight(100) # Limit height to approximately 5 lines
self.layout.addWidget(self.description_input)
# Add URL button
self.add_button = QPushButton("Save")
self.add_button.clicked.connect(self.add_url)
self.layout.addWidget(self.add_button)
# Add space between the Add URL button and URL list
self.layout.addSpacerItem(QSpacerItem(0, 10, QSizePolicy.Minimum, QSizePolicy.Fixed))
# Group selection and search input
self.group_search_layout = QHBoxLayout()
self.group_combobox = QComboBox()
self.group_search_layout.addWidget(self.group_combobox)
self.search_input = QLineEdit()
self.search_input.setPlaceholderText("Search ...")
self.search_input.textChanged.connect(self.filter_urls)
self.group_search_layout.addWidget(self.search_input)
self.layout.addLayout(self.group_search_layout)
# Ensure the dropdown and search input are of equal width
self.group_combobox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
self.search_input.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
# URL list
self.url_list = QTableWidget(0, 3)
self.url_list.setHorizontalHeaderLabels(['URL', 'Date', 'Actions'])
self.url_list.setColumnWidth(0, 200)
self.url_list.setColumnWidth(1, 100)
self.layout.addWidget(self.url_list)
self.setLayout(self.layout)
self.urls = []
self.groups = set()
# Load settings
self.load_settings()
# Load URLs
self.load_urls()
# Search engines
self.search_engines = {
"Bing": "https://www.bing.com/search?q=",
"Brave": "https://search.brave.com/search?q=",
"DuckDuckGo": "https://duckduckgo.com/?q=",
"Ecosia": "https://www.ecosia.org/search?q=",
"Google": "https://www.google.com/search?q=",
"Startpage": "https://www.startpage.com/do/search?q=",
"Swisscows": "https://swisscows.com/web?query="
}
def update_icon_color(self):
# Heuristic to check if the application is in dark mode
palette = self.palette()
if palette.color(palette.Window).value() < 128:
settings_icon_path = 'assets/cogwheel_dark.svg'
info_icon_path = 'assets/info_dark.svg'
else:
settings_icon_path = 'assets/cogwheel_light.svg'
info_icon_path = 'assets/info_light.svg'
self.settings_button.setIcon(QIcon(settings_icon_path))
self.info_button.setIcon(QIcon(info_icon_path))
def add_url(self):
url = self.url_input.text()
description = self.description_input.toPlainText()
group = self.group_combobox.currentText()
if url:
date_added = QDateTime.currentDateTime().toString(Qt.ISODate)
self.urls.append({'url': url, 'description': description, 'date': date_added, 'group': group})
self.save_urls()
self.update_url_list()
self.url_input.clear()
self.description_input.clear()
def update_url(self):
selected_item = self.url_list.currentItem()
if selected_item:
row = self.url_list.row(selected_item)
new_url = self.url_input.text()
new_description = self.description_input.toPlainText()
self.urls[row]['url'] = new_url
self.urls[row]['description'] = new_description
self.save_urls()
self.update_url_list()
self.url_input.clear()
self.description_input.clear()
def delete_url(self):
selected_item = self.url_list.currentItem()
if selected_item:
row = self.url_list.row(selected_item)
del self.urls[row]
self.save_urls()
self.update_url_list()
def filter_urls(self):
search_term = self.search_input.text().lower()
filtered_urls = [url for url in self.urls if search_term in url['url'].lower() or search_term in url['description'].lower()]
self.update_url_list(filtered_urls)
def update_url_list(self, urls=None):
if urls is None:
urls = self.urls
self.url_list.setRowCount(0)
for url in urls:
row_position = self.url_list.rowCount()
self.url_list.insertRow(row_position)
self.url_list.setItem(row_position, 0, QTableWidgetItem(url['url']))
self.url_list.setItem(row_position, 1, QTableWidgetItem(url['date']))
# Action buttons
actions_layout = QHBoxLayout()
edit_button = QPushButton("Edit")
delete_button = QPushButton("Delete")
edit_button.clicked.connect(lambda ch, row=row_position: self.edit_url(row))
delete_button.clicked.connect(lambda ch, row=row_position: self.remove_url(row))
actions_layout.addWidget(edit_button)
actions_layout.addWidget(delete_button)
actions_widget = QWidget()
actions_widget.setLayout(actions_layout)
self.url_list.setCellWidget(row_position, 2, actions_widget)
def edit_url(self, row):
url = self.urls[row]
self.url_input.setText(url['url'])
self.description_input.setPlainText(url['description'])
self.urls.pop(row)
def remove_url(self, row):
del self.urls[row]
self.save_urls()
self.update_url_list()
def load_settings(self):
settings_path = 'data/settings.json'
if os.path.exists(settings_path):
if os.path.getsize(settings_path) > 0:
with open(settings_path, 'r') as file:
settings = json.load(file)
self.data_directory = settings.get('data_directory', 'data/')
self.search_engine = settings.get('search_engine', 'Google')
else:
self.data_directory = 'data/'
self.search_engine = 'Google'
self.save_settings()
else:
self.data_directory = 'data/'
self.search_engine = 'Google'
self.save_settings()
def save_settings(self):
settings_path = 'data/settings.json'
os.makedirs(os.path.dirname(settings_path), exist_ok=True)
with open(settings_path, 'w') as file:
settings = {
'data_directory': self.data_directory,
'search_engine': self.search_engine
}
json.dump(settings, file)
def load_urls(self):
urls_path = os.path.join(self.data_directory, 'urls.json')
if os.path.exists(urls_path):
with open(urls_path, 'r') as file:
self.urls = json.load(file)
self.update_url_list()
def save_urls(self):
urls_path = os.path.join(self.data_directory, 'urls.json')
os.makedirs(os.path.dirname(urls_path), exist_ok=True)
with open(urls_path, 'w') as file:
json.dump(self.urls, file)
def show_info_dialog(self):
dialog = QDialog(self)
dialog.setWindowTitle("About tabRemember")
layout = QVBoxLayout()
# SVG logo
svg_widget = QSvgWidget('assets/logo.svg')
svg_widget.setFixedSize(256, 256)
layout.addWidget(svg_widget, alignment=Qt.AlignCenter)
# Written by label
written_by_label = QLabel("Written by Axel Rafn Benediktsson")
layout.addWidget(written_by_label, alignment=Qt.AlignCenter)
# Link label
link_label = QLabel()
link_label.setText('<a href="https://git.axelrafn.is/axelrafn/tabRemember">https://git.axelrafn.is/axelrafn/tabRemember</a>')
link_label.setOpenExternalLinks(True)
layout.addWidget(link_label, alignment=Qt.AlignCenter)
dialog.setLayout(layout)
dialog.exec_()
def show_settings_dialog(self):
dialog = SettingsDialog(self)
dialog.exec_()
class SettingsDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Settings")
self.setFixedWidth(360) # Set the width of the window to 360 pixels
self.parent = parent
layout = QVBoxLayout()
# Data Directory label
directory_label = QLabel("Data Directory:")
layout.addWidget(directory_label)
# File/Directory selection layout
directory_layout = QHBoxLayout()
self.directory_input = QLineEdit(self.parent.data_directory)
self.browse_button = QPushButton("Browse...")
self.browse_button.clicked.connect(self.browse_directory)
directory_layout.addWidget(self.directory_input)
directory_layout.addWidget(self.browse_button)
layout.addLayout(directory_layout)
# Add 4 pixels of empty space
layout.addSpacerItem(QSpacerItem(0, 4, QSizePolicy.Minimum, QSizePolicy.Fixed))
# Search engine selection
form_layout = QFormLayout()
self.search_engine_combobox = QComboBox()
search_engines = sorted(self.parent.search_engines.keys())
self.search_engine_combobox.addItems(search_engines)
self.search_engine_combobox.setCurrentText(self.parent.search_engine)
form_layout.addRow("Search Engine:", self.search_engine_combobox)
layout.addLayout(form_layout)
# Save button
self.save_button = QPushButton("Save")
self.save_button.clicked.connect(self.save_settings)
layout.addWidget(self.save_button)
self.setLayout(layout)
def browse_directory(self):
directory = QFileDialog.getExistingDirectory(self, "Select Directory")
if directory:
self.directory_input.setText(directory)
def save_settings(self):
self.parent.data_directory = self.directory_input.text()
self.parent.search_engine = self.search_engine_combobox.currentText()
self.parent.save_settings()
self.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setWindowIcon(QIcon('assets/logo.png')) # Set the application icon using PNG for compatibility
window = URLManager()
window.show()
sys.exit(app.exec_())