Update translations for English and Icelandic languages, changed colors of some icons, added more icons.
This commit is contained in:
442
app-new.py
Normal file
442
app-new.py
Normal file
@ -0,0 +1,442 @@
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QLineEdit, QPushButton, QTextEdit, QMessageBox,
|
||||
QSpacerItem, QSizePolicy, QLabel, QFileDialog, QDialog,
|
||||
QFormLayout, QTableWidget, QTableWidgetItem, QHeaderView,
|
||||
QComboBox
|
||||
)
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtCore import Qt, QDateTime, QUrl
|
||||
from PyQt5.QtSvg import QSvgWidget
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
|
||||
|
||||
class URLSearch:
|
||||
def __init__(self, url, search_engine_url):
|
||||
self.url = url
|
||||
self.search_engine_url = search_engine_url
|
||||
self.filters_dir = 'filters/'
|
||||
self.available_filters = self.load_filters()
|
||||
|
||||
def load_filters(self):
|
||||
filters = {}
|
||||
for file_name in os.listdir(self.filters_dir):
|
||||
if file_name.endswith('.filter.py'):
|
||||
domain = '.'.join(file_name.split('.')[:-2]) # Extracting domain.tld from domain.tld.filter.py
|
||||
filters[domain] = os.path.join(self.filters_dir, file_name)
|
||||
# print("Loaded filters:", filters) # Debug print
|
||||
return filters
|
||||
|
||||
def extract_terms(self):
|
||||
parsed_url = QUrl(self.url)
|
||||
host_parts = parsed_url.host().split('.')
|
||||
domain = '.'.join(host_parts[-2:]) # Extract full domain including TLD
|
||||
# print("Extracted domain:", domain) # Debug print
|
||||
if domain in self.available_filters:
|
||||
filter_file = self.available_filters[domain]
|
||||
terms = self.apply_filter(filter_file)
|
||||
if terms:
|
||||
return terms
|
||||
return None
|
||||
|
||||
def apply_filter(self, filter_file):
|
||||
try:
|
||||
with open(filter_file, 'r') as file:
|
||||
filter_code = file.read()
|
||||
local_vars = {'url': self.url, 'terms': [], 'QUrl': QUrl}
|
||||
exec(filter_code, {}, local_vars)
|
||||
return local_vars['terms']
|
||||
except Exception as e:
|
||||
print(f"Error applying filter: {e}")
|
||||
return None
|
||||
|
||||
def search(self):
|
||||
terms = self.extract_terms()
|
||||
if terms:
|
||||
search_query = '+'.join(terms)
|
||||
search_url = f"{self.search_engine_url}{search_query}"
|
||||
QDesktopServices.openUrl(QUrl(search_url))
|
||||
else:
|
||||
print("No valid search term extracted, search disabled.")
|
||||
|
||||
class URLManager(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.settings = self.load_settings()
|
||||
self.translations = {}
|
||||
self.load_translations(f'lang/translations_{self.settings["language"].lower()[:2]}.json')
|
||||
self.setWindowTitle(self.translations['title'])
|
||||
self.setGeometry(100, 100, 600, 800)
|
||||
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(self.translations['url_placeholder'])
|
||||
self.url_layout.addWidget(self.url_input)
|
||||
|
||||
self.settings_button = QPushButton()
|
||||
self.settings_button.setFixedSize(24, 24)
|
||||
self.settings_button.clicked.connect(self.show_settings_dialog)
|
||||
self.url_layout.addWidget(self.settings_button)
|
||||
|
||||
self.info_button = QPushButton()
|
||||
self.info_button.setFixedSize(24, 24)
|
||||
self.info_button.clicked.connect(self.show_info_dialog)
|
||||
self.url_layout.addWidget(self.info_button)
|
||||
|
||||
self.layout.addLayout(self.url_layout)
|
||||
|
||||
self.update_icon_color()
|
||||
|
||||
self.description_input = QTextEdit()
|
||||
self.description_input.setPlaceholderText(self.translations['description_placeholder'])
|
||||
self.description_input.setMaximumHeight(100)
|
||||
self.layout.addWidget(self.description_input)
|
||||
|
||||
self.add_button = QPushButton(self.translations['save_url_button'])
|
||||
self.add_button.clicked.connect(self.add_url)
|
||||
self.layout.addWidget(self.add_button)
|
||||
|
||||
self.layout.addSpacerItem(QSpacerItem(0, 10, QSizePolicy.Minimum, QSizePolicy.Fixed))
|
||||
|
||||
self.search_layout = QHBoxLayout()
|
||||
self.search_input = QLineEdit()
|
||||
self.search_input.setPlaceholderText(self.translations['search_placeholder'])
|
||||
self.search_input.textChanged.connect(self.filter_urls)
|
||||
self.search_layout.addWidget(self.search_input)
|
||||
self.layout.addLayout(self.search_layout)
|
||||
|
||||
self.search_input.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||||
|
||||
self.url_list = QTableWidget()
|
||||
self.url_list.setColumnCount(3)
|
||||
self.url_list.setHorizontalHeaderLabels([self.translations['url_placeholder'], self.translations['column_date'], self.translations['column_actions']])
|
||||
self.url_list.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
|
||||
self.url_list.setColumnWidth(1, 150)
|
||||
self.url_list.setColumnWidth(2, 100)
|
||||
self.layout.addWidget(self.url_list)
|
||||
|
||||
self.setLayout(self.layout)
|
||||
|
||||
self.urls = []
|
||||
|
||||
self.load_urls()
|
||||
|
||||
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 load_translations(self, file_path):
|
||||
if os.path.exists(file_path):
|
||||
with open(file_path, 'r') as file:
|
||||
data = json.load(file)
|
||||
self.translations = data['translations']
|
||||
|
||||
def update_icon_color(self):
|
||||
palette = self.palette()
|
||||
if palette.color(palette.Window).value() < 128:
|
||||
settings_icon_path = 'assets/cogwheel_dark.svg'
|
||||
info_icon_path = 'assets/info_dark.svg'
|
||||
self.action_icons = {
|
||||
"search": 'assets/search_dark.svg',
|
||||
"delete": 'assets/delete_dark.svg'
|
||||
}
|
||||
else:
|
||||
settings_icon_path = 'assets/cogwheel_light.svg'
|
||||
info_icon_path = 'assets/info_light.svg'
|
||||
self.action_icons = {
|
||||
"search": 'assets/search_light.svg',
|
||||
"delete": 'assets/delete_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()
|
||||
if url:
|
||||
date_added = QDateTime.currentDateTime().toString(Qt.ISODate)
|
||||
url_data = {'url': url, 'description': description, 'date': date_added}
|
||||
self.urls.append(url_data)
|
||||
self.save_url(url_data)
|
||||
self.update_url_list()
|
||||
self.url_input.clear()
|
||||
self.description_input.clear()
|
||||
|
||||
def save_url(self, url_data):
|
||||
url_path = os.path.join(self.settings['data_directory'], 'urls.json')
|
||||
if os.path.exists(url_path):
|
||||
with open(url_path, 'r') as file:
|
||||
url_list = json.load(file)
|
||||
else:
|
||||
url_list = []
|
||||
url_list.append(url_data)
|
||||
with open(url_path, 'w') as file:
|
||||
json.dump(url_list, file)
|
||||
|
||||
def update_url(self):
|
||||
selected_item = self.url_list.currentItem()
|
||||
if selected_item:
|
||||
row = selected_item.row()
|
||||
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, row):
|
||||
reply = QMessageBox.question(self, self.translations['delete_confirmation_title'], self.translations['delete_confirmation'],
|
||||
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
||||
if reply == QMessageBox.Yes:
|
||||
del self.urls[row]
|
||||
self.save_urls()
|
||||
self.update_url_list()
|
||||
|
||||
def filter_urls(self):
|
||||
search_term = self.search_input.text().strip().lower()
|
||||
if not search_term:
|
||||
self.update_url_list()
|
||||
return
|
||||
|
||||
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):
|
||||
self.url_list.setRowCount(0)
|
||||
urls_to_display = urls if urls else self.urls
|
||||
for row, url in enumerate(urls_to_display):
|
||||
self.url_list.insertRow(row)
|
||||
self.url_list.setItem(row, 0, QTableWidgetItem(url['url']))
|
||||
formatted_date = self.format_date(url['date'])
|
||||
self.url_list.setItem(row, 1, QTableWidgetItem(formatted_date))
|
||||
|
||||
actions_layout = QHBoxLayout()
|
||||
|
||||
search_button = QPushButton()
|
||||
search_button.setIcon(QIcon(self.action_icons["search"]))
|
||||
search_button.setFixedSize(18, 18)
|
||||
search_button.setStyleSheet("border: none; padding: 0px;")
|
||||
search_button.clicked.connect(lambda _, url=url['url']: self.search_url(url))
|
||||
actions_layout.addWidget(search_button)
|
||||
|
||||
delete_button = QPushButton()
|
||||
delete_button.setIcon(QIcon(self.action_icons["delete"]))
|
||||
delete_button.setFixedSize(18, 18)
|
||||
delete_button.setStyleSheet("border: none; padding: 0px;")
|
||||
delete_button.clicked.connect(lambda _, row=row: self.delete_url(row))
|
||||
actions_layout.addWidget(delete_button)
|
||||
|
||||
actions_widget = QWidget()
|
||||
actions_widget.setLayout(actions_layout)
|
||||
|
||||
self.url_list.setCellWidget(row, 2, actions_widget)
|
||||
|
||||
vertical_header = self.url_list.verticalHeader()
|
||||
vertical_header.setVisible(False)
|
||||
|
||||
def format_date(self, date_str):
|
||||
date = QDateTime.fromString(date_str, Qt.ISODate)
|
||||
if self.settings['date_format'] == 'Nerdy':
|
||||
return date.toString("yyyy-MM-ddTHH:mm:ss")
|
||||
elif self.settings['date_format'] == 'Normal':
|
||||
return date.toString("dd/MM/yy @ HH:mm")
|
||||
elif self.settings['date_format'] == 'Murica!':
|
||||
return date.toString("MM/dd/yyyy hh:mm AP")
|
||||
else:
|
||||
return date_str
|
||||
|
||||
def search_url(self, url):
|
||||
search_engine_url = self.search_engines[self.settings['search_engine']]
|
||||
url_search = URLSearch(url, search_engine_url)
|
||||
url_search.search()
|
||||
|
||||
def load_settings(self):
|
||||
settings_path = 'data/settings.json'
|
||||
if os.path.exists(settings_path):
|
||||
with open(settings_path, 'r') as file:
|
||||
settings = json.load(file)
|
||||
self.data_directory = settings['data_directory']
|
||||
self.search_engine = settings['search_engine']
|
||||
self.date_format = settings.get('date_format', 'Nerdy')
|
||||
self.language = settings.get('language', 'English')
|
||||
return settings
|
||||
else:
|
||||
default_settings = {
|
||||
'data_directory': 'data/',
|
||||
'search_engine': 'Google',
|
||||
'date_format': 'Nerdy',
|
||||
'language': 'English'
|
||||
}
|
||||
self.save_settings(default_settings)
|
||||
return default_settings
|
||||
|
||||
def save_settings(self, settings=None):
|
||||
settings_path = 'data/settings.json'
|
||||
os.makedirs(os.path.dirname(settings_path), exist_ok=True)
|
||||
if settings is None:
|
||||
settings = {
|
||||
'data_directory': self.data_directory,
|
||||
'search_engine': self.search_engine,
|
||||
'date_format': self.date_format,
|
||||
'language': self.language
|
||||
}
|
||||
with open(settings_path, 'w') as file:
|
||||
json.dump(settings, file)
|
||||
|
||||
def load_urls(self):
|
||||
url_path = os.path.join(self.settings['data_directory'], 'urls.json')
|
||||
if os.path.exists(url_path):
|
||||
with open(url_path, 'r') as file:
|
||||
self.urls = json.load(file)
|
||||
self.update_url_list()
|
||||
|
||||
def save_urls(self):
|
||||
url_path = os.path.join(self.settings['data_directory'], 'urls.json')
|
||||
with open(url_path, 'w') as file:
|
||||
json.dump(self.urls, file)
|
||||
|
||||
def show_info_dialog(self):
|
||||
dialog = QDialog(self)
|
||||
dialog.setWindowTitle(self.translations['about_title'])
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
svg_widget = QSvgWidget('assets/logo.svg')
|
||||
svg_widget.setFixedSize(256, 256)
|
||||
layout.addWidget(svg_widget, alignment=Qt.AlignCenter)
|
||||
|
||||
written_by_label = QLabel(self.translations['developed_by'])
|
||||
layout.addWidget(written_by_label, alignment=Qt.AlignCenter)
|
||||
|
||||
link_label = QLabel()
|
||||
link_label.setText(f'<a href="{self.translations["repository_link"]}">{self.translations["repository_link_name"]}</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)
|
||||
if dialog.exec_() == QDialog.Accepted:
|
||||
self.settings = self.load_settings()
|
||||
self.update_url_list()
|
||||
|
||||
class SettingsDialog(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle(parent.translations['settings_title'])
|
||||
self.setFixedWidth(360)
|
||||
|
||||
self.parent = parent
|
||||
|
||||
layout = QVBoxLayout()
|
||||
|
||||
directory_label = QLabel(parent.translations['data_directory_label'])
|
||||
layout.addWidget(directory_label)
|
||||
|
||||
directory_layout = QHBoxLayout()
|
||||
self.directory_input = QLineEdit(self.parent.settings['data_directory'])
|
||||
self.browse_button = QPushButton(self.parent.translations['browse_button'])
|
||||
self.browse_button.clicked.connect(self.browse_directory)
|
||||
directory_layout.addWidget(self.directory_input)
|
||||
directory_layout.addWidget(self.browse_button)
|
||||
layout.addLayout(directory_layout)
|
||||
|
||||
layout.addSpacerItem(QSpacerItem(0, 4, QSizePolicy.Minimum, QSizePolicy.Fixed))
|
||||
|
||||
form_layout = QFormLayout()
|
||||
|
||||
# Populate Search Engine Combobox
|
||||
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.settings['search_engine'])
|
||||
form_layout.addRow(self.parent.translations['search_engine_label'], self.search_engine_combobox)
|
||||
|
||||
# Populate Date Format Combobox
|
||||
self.date_format_combobox = QComboBox()
|
||||
self.date_format_combobox.addItems(["Nerdy", "Normal", "Murica!"])
|
||||
self.date_format_combobox.setCurrentText(self.parent.settings['date_format'])
|
||||
form_layout.addRow(self.parent.translations['date_format_label'], self.date_format_combobox)
|
||||
|
||||
# Populate Language Combobox
|
||||
self.language_combobox = QComboBox()
|
||||
self.populate_language_combobox()
|
||||
current_language_code = self.parent.settings['language']
|
||||
self.set_current_language(current_language_code)
|
||||
form_layout.addRow(self.parent.translations['language_label'], self.language_combobox)
|
||||
|
||||
layout.addLayout(form_layout)
|
||||
|
||||
self.save_button = QPushButton(self.parent.translations['save_button'])
|
||||
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 populate_language_combobox(self):
|
||||
lang_dir = 'lang/'
|
||||
language_files = [f for f in os.listdir(lang_dir) if f.startswith('translations_') and f.endswith('.json')]
|
||||
self.language_map = {}
|
||||
|
||||
for file_name in language_files:
|
||||
file_path = os.path.join(lang_dir, file_name)
|
||||
with open(file_path, 'r') as file:
|
||||
data = json.load(file)
|
||||
language_name = data.get('language', 'Unknown')
|
||||
language_code = file_name[len('translations_'):-len('.json')]
|
||||
display_text = f"{language_name} ({language_code})"
|
||||
self.language_combobox.addItem(display_text)
|
||||
self.language_map[language_code] = display_text
|
||||
|
||||
def set_current_language(self, language_code):
|
||||
if language_code in self.language_map:
|
||||
display_text = self.language_map[language_code]
|
||||
index = self.language_combobox.findText(display_text)
|
||||
if index != -1:
|
||||
self.language_combobox.setCurrentIndex(index)
|
||||
|
||||
def save_settings(self):
|
||||
selected_language = self.language_combobox.currentText()
|
||||
language_code = selected_language.split('(')[-1].strip(' )') # Extract language code from "(en)" format
|
||||
old_language = self.parent.settings['language']
|
||||
self.parent.settings['data_directory'] = self.directory_input.text()
|
||||
self.parent.settings['search_engine'] = self.search_engine_combobox.currentText()
|
||||
self.parent.settings['date_format'] = self.date_format_combobox.currentText()
|
||||
self.parent.settings['language'] = language_code
|
||||
self.parent.save_settings(self.parent.settings)
|
||||
|
||||
if old_language != language_code:
|
||||
QMessageBox.information(self, self.parent.translations['restart_required_title'], self.parent.translations['restart_required_message'])
|
||||
|
||||
self.accept()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
window = URLManager()
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
||||
Reference in New Issue
Block a user