Compare commits
7 Commits
fa76fe14b6
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 36b61c0600 | |||
| 3a52b5793f | |||
| 7cbb95b230 | |||
| 29c4f64cf8 | |||
| 57024c48a1 | |||
| b1d72dde93 | |||
| f9ec284ec2 |
118
README.md
118
README.md
@ -1,12 +1,124 @@
|
|||||||
# tabRemember
|
# tabRemember
|
||||||
|
|
||||||
A open-source app written in Python using CustomTkinter for managing links for SimsX players who use custom content.
|
A open-source app written in Python using PyQt6 for managing links for Sims players who use custom content, and for others who need a nice, simple and little bookmark manager outside the browser.
|
||||||
|
|
||||||
# Wait, what?
|
# Wait, what?
|
||||||
|
|
||||||
My wife is one of those people and I've seen how her browsers are, she has maxed out the tabs in the Safari browser in her iPhone and is always stressed that she might lose them at any given moment. Her computer is no better..
|
My wife is one of those people and I've seen how her browsers are, she has maxed out the tabs in the Safari browser in her iPhone and is always stressed that she might lose them at any given moment. Her computer is no better..
|
||||||
|
|
||||||
So this project is to create a cross platform app written in Python, relying on CustomTkinter for theming and looks.
|
So this project is to create a cross platform app written in Python, relying on PyQt6 for theming and looks.
|
||||||
I plan to have it multilingual as well since not everyone uses the same language.
|
I plan to have it multilingual as well since not everyone uses the same language.
|
||||||
|
|
||||||
For the time being the languages at the start and while I build this app up towards the first release, I will only offer English and Icelandic.
|
For the time being the languages at the start and while I build this app up towards the first release, I will only offer English and Icelandic.
|
||||||
Later on people are welcome to offer their translations for their language as well and I will happilly add them to the codebase.
|
Later on people are welcome to offer their translations for their language as well and I will happilly add them to the codebase.
|
||||||
|
|
||||||
|
The program offers filters for domains, so you can break up the URL you have in your bookmarks and search for it. This idea came to be since a lot of custom content creators, especially on tumblr, tend to delete the original post and create a new one when they update the custom content they made. Normally that would leave you with a broken link, but with these filters you can search (with your preferred search engine mind you!) for key words from the URL, such as the domain name itself, the user who psoted it orignally, the title of the post. With these filters you can easilly find what you bookmarked even when you have a broken link.
|
||||||
|
|
||||||
|
# tabRemember - URL Manager
|
||||||
|
|
||||||
|
**tabRemember** is a simple and effective URL manager that helps you store, search, and manage your favorite URLs with descriptions and custom search functionality.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Save URLs with descriptions.
|
||||||
|
- View, edit, and delete saved URLs.
|
||||||
|
- Search saved URLs using different search engines.
|
||||||
|
- Auto-search URLs using filters to extract meaningful search terms.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
1. **Install Python:** Ensure you have Python installed on your system. You can download it from [python.org](https://www.python.org/).
|
||||||
|
|
||||||
|
2. **Install Required Libraries:**
|
||||||
|
```sh
|
||||||
|
pip install PyQt6
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Download and Run the App:**
|
||||||
|
- Download the app files from the repository, either by downloading a zip file from here or by running the following command with git:
|
||||||
|
```sh
|
||||||
|
git clone https://git.axelrafn.is/axelrafn/tabRemember.git
|
||||||
|
```
|
||||||
|
- Then go into the directory you just downloaded and run the application using:
|
||||||
|
```sh
|
||||||
|
python app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Main Interface
|
||||||
|
|
||||||
|
When you start the app, you'll see the main window with the following components:
|
||||||
|
|
||||||
|
1. **URL Input:** A text box to enter the URL you want to save.
|
||||||
|
2. **Description Input:** A text area to add a description or notes about the URL.
|
||||||
|
3. **Save URL Button:** Click this button to save the URL and description.
|
||||||
|
4. **Search Input:** A text box to search through your saved URLs.
|
||||||
|
5. **URL List:** A table that displays your saved URLs with columns for the URL, date, and actions.
|
||||||
|
|
||||||
|
### Actions Column
|
||||||
|
|
||||||
|
Each URL in the list has an "Actions" column with the following buttons:
|
||||||
|
|
||||||
|
1. **Visit Button (🔗):** Opens the URL in your default web browser.
|
||||||
|
2. **Info Button (ℹ️):** Opens a dialog to view and edit the URL and its description.
|
||||||
|
3. **Search Button (🔍):** Searches for the URL using predefined filters. If no filter is found, it opens the URL directly.
|
||||||
|
4. **Delete Button (🗑️):** Deletes the URL from your list.
|
||||||
|
|
||||||
|
### Settings
|
||||||
|
|
||||||
|
Click the settings icon (⚙️) to open the settings dialog where you can:
|
||||||
|
|
||||||
|
- **Change Data Directory:** Set the directory where your URLs are saved.
|
||||||
|
- **Select Search Engine:** Choose your preferred search engine.
|
||||||
|
- **Set Date Format:** Choose the format for displaying dates.
|
||||||
|
- **Change Language:** Change the app's language. (Note: You need to restart the app for language changes to take effect.)
|
||||||
|
|
||||||
|
### About
|
||||||
|
|
||||||
|
Click the info icon (ℹ️) to open the about dialog, which provides information about the developer and a link to the project's repository.
|
||||||
|
|
||||||
|
## Filters
|
||||||
|
|
||||||
|
The app supports custom filters for extracting search terms from URLs. Filters are stored in the `filters` directory and named as `domain.tld.filter.py`.
|
||||||
|
|
||||||
|
### Creating a Filter
|
||||||
|
|
||||||
|
1. **Create a Filter File:**
|
||||||
|
- Name the file in the format `domain.tld.filter.py`.
|
||||||
|
- Example: For `tumblr.com`, the file name should be `tumblr.com.filter.py`.
|
||||||
|
|
||||||
|
2. **Define the Filter:**
|
||||||
|
- Use Python code to define how to extract search terms.
|
||||||
|
- Example for `tumblr.com`:
|
||||||
|
```python
|
||||||
|
from urllib.parse import urlparse, unquote
|
||||||
|
|
||||||
|
url_parts = urlparse(url)
|
||||||
|
path_segments = unquote(url_parts.path).strip('/').split('/')
|
||||||
|
terms = [url_parts.netloc.split('.')[0]] + path_segments
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
### Saving a URL
|
||||||
|
|
||||||
|
1. Enter the URL in the URL input box.
|
||||||
|
2. Add a description in the description text area.
|
||||||
|
3. Click the "Save URL" button.
|
||||||
|
|
||||||
|
### Searching for a URL
|
||||||
|
|
||||||
|
1. Enter search terms in the search input box.
|
||||||
|
2. The URL list will filter results as you type.
|
||||||
|
|
||||||
|
### Viewing and Editing a URL
|
||||||
|
|
||||||
|
1. Click the info button (ℹ️) next to the URL.
|
||||||
|
2. A dialog will open showing the URL and description.
|
||||||
|
3. Edit the URL or description and click "Update" to save changes.
|
||||||
|
|
||||||
|
### Deleting a URL
|
||||||
|
|
||||||
|
1. Click the delete button (🗑️) next to the URL.
|
||||||
|
2. Confirm the deletion in the dialog that appears.
|
||||||
|
|||||||
196
app-new.py
196
app-new.py
@ -1,19 +1,16 @@
|
|||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
from PyQt6.QtWidgets import (
|
||||||
from PyQt5.QtWidgets import (
|
|
||||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
|
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
|
||||||
QLineEdit, QPushButton, QTextEdit, QMessageBox,
|
QLineEdit, QPushButton, QTextEdit, QMessageBox,
|
||||||
QSpacerItem, QSizePolicy, QLabel, QFileDialog, QDialog,
|
QSpacerItem, QSizePolicy, QLabel, QFileDialog, QDialog,
|
||||||
QFormLayout, QTableWidget, QTableWidgetItem, QHeaderView,
|
QFormLayout, QTableWidget, QTableWidgetItem, QHeaderView,
|
||||||
QComboBox
|
QComboBox, QAbstractItemView
|
||||||
)
|
)
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt6.QtGui import QIcon, QDesktopServices
|
||||||
from PyQt5.QtCore import Qt, QDateTime, QUrl
|
from PyQt6.QtCore import Qt, QDateTime, QUrl
|
||||||
from PyQt5.QtSvg import QSvgWidget
|
from PyQt6.QtSvgWidgets import QSvgWidget
|
||||||
from PyQt5.QtGui import QDesktopServices
|
|
||||||
|
|
||||||
|
|
||||||
class URLSearch:
|
class URLSearch:
|
||||||
def __init__(self, url, search_engine_url):
|
def __init__(self, url, search_engine_url):
|
||||||
@ -28,14 +25,14 @@ class URLSearch:
|
|||||||
if file_name.endswith('.filter.py'):
|
if file_name.endswith('.filter.py'):
|
||||||
domain = '.'.join(file_name.split('.')[:-2]) # Extracting domain.tld from domain.tld.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)
|
filters[domain] = os.path.join(self.filters_dir, file_name)
|
||||||
# print("Loaded filters:", filters) # Debug print
|
print("Loaded filters:", filters) # Debug print
|
||||||
return filters
|
return filters
|
||||||
|
|
||||||
def extract_terms(self):
|
def extract_terms(self):
|
||||||
parsed_url = QUrl(self.url)
|
parsed_url = QUrl(self.url)
|
||||||
host_parts = parsed_url.host().split('.')
|
host_parts = parsed_url.host().split('.')
|
||||||
domain = '.'.join(host_parts[-2:]) # Extract full domain including TLD
|
domain = '.'.join(host_parts[-2:]) # Extract full domain including TLD
|
||||||
# print("Extracted domain:", domain) # Debug print
|
print("Extracted domain:", domain) # Debug print
|
||||||
if domain in self.available_filters:
|
if domain in self.available_filters:
|
||||||
filter_file = self.available_filters[domain]
|
filter_file = self.available_filters[domain]
|
||||||
terms = self.apply_filter(filter_file)
|
terms = self.apply_filter(filter_file)
|
||||||
@ -61,7 +58,7 @@ class URLSearch:
|
|||||||
search_url = f"{self.search_engine_url}{search_query}"
|
search_url = f"{self.search_engine_url}{search_query}"
|
||||||
QDesktopServices.openUrl(QUrl(search_url))
|
QDesktopServices.openUrl(QUrl(search_url))
|
||||||
else:
|
else:
|
||||||
print("No valid search term extracted, search disabled.")
|
QDesktopServices.openUrl(QUrl(self.url)) # Open the URL directly if no valid search term is extracted
|
||||||
|
|
||||||
class URLManager(QWidget):
|
class URLManager(QWidget):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -70,25 +67,29 @@ class URLManager(QWidget):
|
|||||||
self.settings = self.load_settings()
|
self.settings = self.load_settings()
|
||||||
self.translations = {}
|
self.translations = {}
|
||||||
self.load_translations(f'lang/translations_{self.settings["language"].lower()[:2]}.json')
|
self.load_translations(f'lang/translations_{self.settings["language"].lower()[:2]}.json')
|
||||||
self.setWindowTitle(self.translations['title'])
|
self.setWindowTitle(self.get_translation('title', 'URL Manager'))
|
||||||
self.setGeometry(100, 100, 600, 800)
|
self.setGeometry(100, 100, 600, 600)
|
||||||
self.setWindowIcon(QIcon('assets/logo.png'))
|
self.setWindowIcon(QIcon('assets/logo.png'))
|
||||||
|
|
||||||
|
self.load_stylesheet("style-new.qss") # Load and apply the stylesheet
|
||||||
|
|
||||||
self.layout = QVBoxLayout()
|
self.layout = QVBoxLayout()
|
||||||
|
|
||||||
# URL input and settings button layout
|
# URL input and settings button layout
|
||||||
self.url_layout = QHBoxLayout()
|
self.url_layout = QHBoxLayout()
|
||||||
self.url_input = QLineEdit()
|
self.url_input = QLineEdit()
|
||||||
self.url_input.setPlaceholderText(self.translations['url_placeholder'])
|
self.url_input.setPlaceholderText(self.get_translation('url_placeholder', 'Enter URL'))
|
||||||
self.url_layout.addWidget(self.url_input)
|
self.url_layout.addWidget(self.url_input)
|
||||||
|
|
||||||
self.settings_button = QPushButton()
|
self.settings_button = QPushButton()
|
||||||
self.settings_button.setFixedSize(24, 24)
|
self.settings_button.setFixedSize(28, 28)
|
||||||
|
self.settings_button.setStyleSheet("padding: 5px;")
|
||||||
self.settings_button.clicked.connect(self.show_settings_dialog)
|
self.settings_button.clicked.connect(self.show_settings_dialog)
|
||||||
self.url_layout.addWidget(self.settings_button)
|
self.url_layout.addWidget(self.settings_button)
|
||||||
|
|
||||||
self.info_button = QPushButton()
|
self.info_button = QPushButton()
|
||||||
self.info_button.setFixedSize(24, 24)
|
self.info_button.setFixedSize(28, 28)
|
||||||
|
self.info_button.setStyleSheet("padding: 5px;")
|
||||||
self.info_button.clicked.connect(self.show_info_dialog)
|
self.info_button.clicked.connect(self.show_info_dialog)
|
||||||
self.url_layout.addWidget(self.info_button)
|
self.url_layout.addWidget(self.info_button)
|
||||||
|
|
||||||
@ -97,31 +98,36 @@ class URLManager(QWidget):
|
|||||||
self.update_icon_color()
|
self.update_icon_color()
|
||||||
|
|
||||||
self.description_input = QTextEdit()
|
self.description_input = QTextEdit()
|
||||||
self.description_input.setPlaceholderText(self.translations['description_placeholder'])
|
self.description_input.setPlaceholderText(self.get_translation('description_placeholder', 'Enter Description or Thoughts'))
|
||||||
self.description_input.setMaximumHeight(100)
|
self.description_input.setMaximumHeight(100)
|
||||||
self.layout.addWidget(self.description_input)
|
self.layout.addWidget(self.description_input)
|
||||||
|
|
||||||
self.add_button = QPushButton(self.translations['save_url_button'])
|
self.add_button = QPushButton(self.get_translation('save_url_button', 'Save URL'))
|
||||||
self.add_button.clicked.connect(self.add_url)
|
self.add_button.clicked.connect(self.add_url)
|
||||||
self.layout.addWidget(self.add_button)
|
self.layout.addWidget(self.add_button)
|
||||||
|
|
||||||
self.layout.addSpacerItem(QSpacerItem(0, 10, QSizePolicy.Minimum, QSizePolicy.Fixed))
|
self.layout.addSpacerItem(QSpacerItem(0, 10, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed))
|
||||||
|
|
||||||
self.search_layout = QHBoxLayout()
|
self.search_layout = QHBoxLayout()
|
||||||
self.search_input = QLineEdit()
|
self.search_input = QLineEdit()
|
||||||
self.search_input.setPlaceholderText(self.translations['search_placeholder'])
|
self.search_input.setPlaceholderText(self.get_translation('search_placeholder', 'Search...'))
|
||||||
self.search_input.textChanged.connect(self.filter_urls)
|
self.search_input.textChanged.connect(self.filter_urls)
|
||||||
self.search_layout.addWidget(self.search_input)
|
self.search_layout.addWidget(self.search_input)
|
||||||
self.layout.addLayout(self.search_layout)
|
self.layout.addLayout(self.search_layout)
|
||||||
|
|
||||||
self.search_input.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
self.search_input.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||||
|
|
||||||
self.url_list = QTableWidget()
|
self.url_list = QTableWidget()
|
||||||
self.url_list.setColumnCount(3)
|
self.url_list.setColumnCount(3)
|
||||||
self.url_list.setHorizontalHeaderLabels([self.translations['url_placeholder'], self.translations['column_date'], self.translations['column_actions']])
|
self.url_list.setHorizontalHeaderLabels([
|
||||||
self.url_list.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
|
self.get_translation('column_url', 'URL'),
|
||||||
|
self.get_translation('column_date', 'Date'),
|
||||||
|
self.get_translation('column_actions', 'Actions')
|
||||||
|
])
|
||||||
|
self.url_list.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
|
||||||
self.url_list.setColumnWidth(1, 150)
|
self.url_list.setColumnWidth(1, 150)
|
||||||
self.url_list.setColumnWidth(2, 100)
|
self.url_list.setColumnWidth(2, 100)
|
||||||
|
self.url_list.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
|
||||||
self.layout.addWidget(self.url_list)
|
self.layout.addWidget(self.url_list)
|
||||||
|
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
@ -140,18 +146,28 @@ class URLManager(QWidget):
|
|||||||
"Swisscows": "https://swisscows.com/web?query="
|
"Swisscows": "https://swisscows.com/web?query="
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def load_stylesheet(self, stylesheet_path):
|
||||||
|
if os.path.exists(stylesheet_path):
|
||||||
|
with open(stylesheet_path, "r") as file:
|
||||||
|
self.setStyleSheet(file.read())
|
||||||
|
|
||||||
def load_translations(self, file_path):
|
def load_translations(self, file_path):
|
||||||
if os.path.exists(file_path):
|
if os.path.exists(file_path):
|
||||||
with open(file_path, 'r') as file:
|
with open(file_path, 'r') as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
self.translations = data['translations']
|
self.translations = data['translations']
|
||||||
|
|
||||||
|
def get_translation(self, key, default):
|
||||||
|
return self.translations.get(key, default)
|
||||||
|
|
||||||
def update_icon_color(self):
|
def update_icon_color(self):
|
||||||
palette = self.palette()
|
palette = self.palette()
|
||||||
if palette.color(palette.Window).value() < 128:
|
if palette.color(palette.ColorRole.Window).value() < 128:
|
||||||
settings_icon_path = 'assets/cogwheel_dark.svg'
|
settings_icon_path = 'assets/cogwheel_dark.svg'
|
||||||
info_icon_path = 'assets/info_dark.svg'
|
info_icon_path = 'assets/info_dark.svg'
|
||||||
self.action_icons = {
|
self.action_icons = {
|
||||||
|
"visit": 'assets/visit_dark.svg',
|
||||||
|
"info": 'assets/info_dark.svg',
|
||||||
"search": 'assets/search_dark.svg',
|
"search": 'assets/search_dark.svg',
|
||||||
"delete": 'assets/delete_dark.svg'
|
"delete": 'assets/delete_dark.svg'
|
||||||
}
|
}
|
||||||
@ -159,6 +175,8 @@ class URLManager(QWidget):
|
|||||||
settings_icon_path = 'assets/cogwheel_light.svg'
|
settings_icon_path = 'assets/cogwheel_light.svg'
|
||||||
info_icon_path = 'assets/info_light.svg'
|
info_icon_path = 'assets/info_light.svg'
|
||||||
self.action_icons = {
|
self.action_icons = {
|
||||||
|
"visit": 'assets/visit_light.svg',
|
||||||
|
"info": 'assets/info_light.svg',
|
||||||
"search": 'assets/search_light.svg',
|
"search": 'assets/search_light.svg',
|
||||||
"delete": 'assets/delete_light.svg'
|
"delete": 'assets/delete_light.svg'
|
||||||
}
|
}
|
||||||
@ -170,7 +188,7 @@ class URLManager(QWidget):
|
|||||||
url = self.url_input.text()
|
url = self.url_input.text()
|
||||||
description = self.description_input.toPlainText()
|
description = self.description_input.toPlainText()
|
||||||
if url:
|
if url:
|
||||||
date_added = QDateTime.currentDateTime().toString(Qt.ISODate)
|
date_added = QDateTime.currentDateTime().toString(Qt.DateFormat.ISODate)
|
||||||
url_data = {'url': url, 'description': description, 'date': date_added}
|
url_data = {'url': url, 'description': description, 'date': date_added}
|
||||||
self.urls.append(url_data)
|
self.urls.append(url_data)
|
||||||
self.save_url(url_data)
|
self.save_url(url_data)
|
||||||
@ -189,23 +207,16 @@ class URLManager(QWidget):
|
|||||||
with open(url_path, 'w') as file:
|
with open(url_path, 'w') as file:
|
||||||
json.dump(url_list, file)
|
json.dump(url_list, file)
|
||||||
|
|
||||||
def update_url(self):
|
def update_url(self, row, new_url, new_description):
|
||||||
selected_item = self.url_list.currentItem()
|
self.urls[row]['url'] = new_url
|
||||||
if selected_item:
|
self.urls[row]['description'] = new_description
|
||||||
row = selected_item.row()
|
self.save_urls()
|
||||||
new_url = self.url_input.text()
|
self.update_url_list()
|
||||||
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):
|
def delete_url(self, row):
|
||||||
reply = QMessageBox.question(self, self.translations['delete_confirmation_title'], self.translations['delete_confirmation'],
|
reply = QMessageBox.question(self, self.get_translation('delete_confirmation_title', 'Delete URL'), self.get_translation('delete_confirmation', 'Are you sure you want to delete this URL?'),
|
||||||
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No)
|
||||||
if reply == QMessageBox.Yes:
|
if reply == QMessageBox.StandardButton.Yes:
|
||||||
del self.urls[row]
|
del self.urls[row]
|
||||||
self.save_urls()
|
self.save_urls()
|
||||||
self.update_url_list()
|
self.update_url_list()
|
||||||
@ -226,21 +237,39 @@ class URLManager(QWidget):
|
|||||||
self.url_list.insertRow(row)
|
self.url_list.insertRow(row)
|
||||||
self.url_list.setItem(row, 0, QTableWidgetItem(url['url']))
|
self.url_list.setItem(row, 0, QTableWidgetItem(url['url']))
|
||||||
formatted_date = self.format_date(url['date'])
|
formatted_date = self.format_date(url['date'])
|
||||||
self.url_list.setItem(row, 1, QTableWidgetItem(formatted_date))
|
date_item = QTableWidgetItem(formatted_date)
|
||||||
|
date_item.setTextAlignment(Qt.AlignmentFlag.AlignVCenter)
|
||||||
|
self.url_list.setItem(row, 1, date_item)
|
||||||
|
|
||||||
actions_layout = QHBoxLayout()
|
actions_layout = QHBoxLayout()
|
||||||
|
actions_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
actions_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
|
visit_button = QPushButton()
|
||||||
|
visit_button.setIcon(QIcon(self.action_icons["visit"]))
|
||||||
|
visit_button.setFixedSize(22, 22)
|
||||||
|
visit_button.setStyleSheet("border: none; padding: 3px;")
|
||||||
|
visit_button.clicked.connect(lambda _, url=url['url']: self.visit_url(url))
|
||||||
|
actions_layout.addWidget(visit_button)
|
||||||
|
|
||||||
|
info_button = QPushButton()
|
||||||
|
info_button.setIcon(QIcon(self.action_icons["info"]))
|
||||||
|
info_button.setFixedSize(22, 22)
|
||||||
|
info_button.setStyleSheet("border: none; padding: 3px;")
|
||||||
|
info_button.clicked.connect(lambda _, row=row: self.view_description(row))
|
||||||
|
actions_layout.addWidget(info_button)
|
||||||
|
|
||||||
search_button = QPushButton()
|
search_button = QPushButton()
|
||||||
search_button.setIcon(QIcon(self.action_icons["search"]))
|
search_button.setIcon(QIcon(self.action_icons["search"]))
|
||||||
search_button.setFixedSize(18, 18)
|
search_button.setFixedSize(22, 22)
|
||||||
search_button.setStyleSheet("border: none; padding: 0px;")
|
search_button.setStyleSheet("border: none; padding: 3px;")
|
||||||
search_button.clicked.connect(lambda _, url=url['url']: self.search_url(url))
|
search_button.clicked.connect(lambda _, url=url['url']: self.search_url(url))
|
||||||
actions_layout.addWidget(search_button)
|
actions_layout.addWidget(search_button)
|
||||||
|
|
||||||
delete_button = QPushButton()
|
delete_button = QPushButton()
|
||||||
delete_button.setIcon(QIcon(self.action_icons["delete"]))
|
delete_button.setIcon(QIcon(self.action_icons["delete"]))
|
||||||
delete_button.setFixedSize(18, 18)
|
delete_button.setFixedSize(22, 22)
|
||||||
delete_button.setStyleSheet("border: none; padding: 0px;")
|
delete_button.setStyleSheet("border: none; padding: 3px;")
|
||||||
delete_button.clicked.connect(lambda _, row=row: self.delete_url(row))
|
delete_button.clicked.connect(lambda _, row=row: self.delete_url(row))
|
||||||
actions_layout.addWidget(delete_button)
|
actions_layout.addWidget(delete_button)
|
||||||
|
|
||||||
@ -253,7 +282,7 @@ class URLManager(QWidget):
|
|||||||
vertical_header.setVisible(False)
|
vertical_header.setVisible(False)
|
||||||
|
|
||||||
def format_date(self, date_str):
|
def format_date(self, date_str):
|
||||||
date = QDateTime.fromString(date_str, Qt.ISODate)
|
date = QDateTime.fromString(date_str, Qt.DateFormat.ISODate)
|
||||||
if self.settings['date_format'] == 'Nerdy':
|
if self.settings['date_format'] == 'Nerdy':
|
||||||
return date.toString("yyyy-MM-ddTHH:mm:ss")
|
return date.toString("yyyy-MM-ddTHH:mm:ss")
|
||||||
elif self.settings['date_format'] == 'Normal':
|
elif self.settings['date_format'] == 'Normal':
|
||||||
@ -263,6 +292,13 @@ class URLManager(QWidget):
|
|||||||
else:
|
else:
|
||||||
return date_str
|
return date_str
|
||||||
|
|
||||||
|
def visit_url(self, url):
|
||||||
|
QDesktopServices.openUrl(QUrl(url))
|
||||||
|
|
||||||
|
def view_description(self, row):
|
||||||
|
dialog = DescriptionDialog(self, row)
|
||||||
|
dialog.exec()
|
||||||
|
|
||||||
def search_url(self, url):
|
def search_url(self, url):
|
||||||
search_engine_url = self.search_engines[self.settings['search_engine']]
|
search_engine_url = self.search_engines[self.settings['search_engine']]
|
||||||
url_search = URLSearch(url, search_engine_url)
|
url_search = URLSearch(url, search_engine_url)
|
||||||
@ -315,53 +351,85 @@ class URLManager(QWidget):
|
|||||||
|
|
||||||
def show_info_dialog(self):
|
def show_info_dialog(self):
|
||||||
dialog = QDialog(self)
|
dialog = QDialog(self)
|
||||||
dialog.setWindowTitle(self.translations['about_title'])
|
dialog.setWindowTitle(self.get_translation('about_title', 'About URL Manager'))
|
||||||
|
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
svg_widget = QSvgWidget('assets/logo.svg')
|
svg_widget = QSvgWidget('assets/logo.svg')
|
||||||
svg_widget.setFixedSize(256, 256)
|
svg_widget.setFixedSize(256, 256)
|
||||||
layout.addWidget(svg_widget, alignment=Qt.AlignCenter)
|
layout.addWidget(svg_widget, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
written_by_label = QLabel(self.translations['developed_by'])
|
written_by_label = QLabel(self.get_translation('developed_by', 'Developed by Axel Rafn'))
|
||||||
layout.addWidget(written_by_label, alignment=Qt.AlignCenter)
|
layout.addWidget(written_by_label, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
link_label = QLabel()
|
link_label = QLabel()
|
||||||
link_label.setText(f'<a href="{self.translations["repository_link"]}">{self.translations["repository_link_name"]}</a>')
|
link_label.setText(f'<a href="{self.get_translation("repository_link", "#")}">{self.get_translation("repository_link_name", "Project Home")}</a>')
|
||||||
link_label.setOpenExternalLinks(True)
|
link_label.setOpenExternalLinks(True)
|
||||||
layout.addWidget(link_label, alignment=Qt.AlignCenter)
|
layout.addWidget(link_label, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
dialog.setLayout(layout)
|
dialog.setLayout(layout)
|
||||||
dialog.exec_()
|
dialog.exec()
|
||||||
|
|
||||||
def show_settings_dialog(self):
|
def show_settings_dialog(self):
|
||||||
dialog = SettingsDialog(self)
|
dialog = SettingsDialog(self)
|
||||||
if dialog.exec_() == QDialog.Accepted:
|
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||||
self.settings = self.load_settings()
|
self.settings = self.load_settings()
|
||||||
self.update_url_list()
|
self.update_url_list()
|
||||||
|
|
||||||
|
class DescriptionDialog(QDialog):
|
||||||
|
def __init__(self, parent, row):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.parent = parent
|
||||||
|
self.row = row
|
||||||
|
self.setWindowTitle(self.parent.get_translation('description_title', 'URL Description'))
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
self.url_input = QLineEdit(self.parent.urls[row]['url'])
|
||||||
|
layout.addWidget(self.url_input)
|
||||||
|
|
||||||
|
self.description_input = QTextEdit(self.parent.urls[row]['description'])
|
||||||
|
layout.addWidget(self.description_input)
|
||||||
|
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
self.update_button = QPushButton(self.parent.get_translation('update_button', 'Update'))
|
||||||
|
self.update_button.clicked.connect(self.update_url)
|
||||||
|
self.close_button = QPushButton(self.parent.get_translation('close_button', 'Close'))
|
||||||
|
self.close_button.clicked.connect(self.close)
|
||||||
|
button_layout.addWidget(self.update_button)
|
||||||
|
button_layout.addWidget(self.close_button)
|
||||||
|
layout.addLayout(button_layout)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def update_url(self):
|
||||||
|
new_url = self.url_input.text()
|
||||||
|
new_description = self.description_input.toPlainText()
|
||||||
|
self.parent.update_url(self.row, new_url, new_description)
|
||||||
|
self.accept()
|
||||||
|
|
||||||
class SettingsDialog(QDialog):
|
class SettingsDialog(QDialog):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setWindowTitle(parent.translations['settings_title'])
|
self.setWindowTitle(parent.get_translation('settings_title', 'Settings'))
|
||||||
self.setFixedWidth(360)
|
self.setFixedWidth(360)
|
||||||
|
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
directory_label = QLabel(parent.translations['data_directory_label'])
|
directory_label = QLabel(parent.get_translation('data_directory_label', 'Data Directory:'))
|
||||||
layout.addWidget(directory_label)
|
layout.addWidget(directory_label)
|
||||||
|
|
||||||
directory_layout = QHBoxLayout()
|
directory_layout = QHBoxLayout()
|
||||||
self.directory_input = QLineEdit(self.parent.settings['data_directory'])
|
self.directory_input = QLineEdit(self.parent.settings['data_directory'])
|
||||||
self.browse_button = QPushButton(self.parent.translations['browse_button'])
|
self.browse_button = QPushButton(self.parent.get_translation('browse_button', 'Browse...'))
|
||||||
self.browse_button.clicked.connect(self.browse_directory)
|
self.browse_button.clicked.connect(self.browse_directory)
|
||||||
directory_layout.addWidget(self.directory_input)
|
directory_layout.addWidget(self.directory_input)
|
||||||
directory_layout.addWidget(self.browse_button)
|
directory_layout.addWidget(self.browse_button)
|
||||||
layout.addLayout(directory_layout)
|
layout.addLayout(directory_layout)
|
||||||
|
|
||||||
layout.addSpacerItem(QSpacerItem(0, 4, QSizePolicy.Minimum, QSizePolicy.Fixed))
|
layout.addSpacerItem(QSpacerItem(0, 4, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed))
|
||||||
|
|
||||||
form_layout = QFormLayout()
|
form_layout = QFormLayout()
|
||||||
|
|
||||||
@ -370,24 +438,24 @@ class SettingsDialog(QDialog):
|
|||||||
search_engines = sorted(self.parent.search_engines.keys())
|
search_engines = sorted(self.parent.search_engines.keys())
|
||||||
self.search_engine_combobox.addItems(search_engines)
|
self.search_engine_combobox.addItems(search_engines)
|
||||||
self.search_engine_combobox.setCurrentText(self.parent.settings['search_engine'])
|
self.search_engine_combobox.setCurrentText(self.parent.settings['search_engine'])
|
||||||
form_layout.addRow(self.parent.translations['search_engine_label'], self.search_engine_combobox)
|
form_layout.addRow(self.parent.get_translation('search_engine_label', 'Search Engine:'), self.search_engine_combobox)
|
||||||
|
|
||||||
# Populate Date Format Combobox
|
# Populate Date Format Combobox
|
||||||
self.date_format_combobox = QComboBox()
|
self.date_format_combobox = QComboBox()
|
||||||
self.date_format_combobox.addItems(["Nerdy", "Normal", "Murica!"])
|
self.date_format_combobox.addItems(["Nerdy", "Normal", "Murica!"])
|
||||||
self.date_format_combobox.setCurrentText(self.parent.settings['date_format'])
|
self.date_format_combobox.setCurrentText(self.parent.settings['date_format'])
|
||||||
form_layout.addRow(self.parent.translations['date_format_label'], self.date_format_combobox)
|
form_layout.addRow(self.parent.get_translation('date_format_label', 'Date Format:'), self.date_format_combobox)
|
||||||
|
|
||||||
# Populate Language Combobox
|
# Populate Language Combobox
|
||||||
self.language_combobox = QComboBox()
|
self.language_combobox = QComboBox()
|
||||||
self.populate_language_combobox()
|
self.populate_language_combobox()
|
||||||
current_language_code = self.parent.settings['language']
|
current_language_code = self.parent.settings['language']
|
||||||
self.set_current_language(current_language_code)
|
self.set_current_language(current_language_code)
|
||||||
form_layout.addRow(self.parent.translations['language_label'], self.language_combobox)
|
form_layout.addRow(self.parent.get_translation('language_label', 'Language:'), self.language_combobox)
|
||||||
|
|
||||||
layout.addLayout(form_layout)
|
layout.addLayout(form_layout)
|
||||||
|
|
||||||
self.save_button = QPushButton(self.parent.translations['save_button'])
|
self.save_button = QPushButton(self.parent.get_translation('save_button', 'Save'))
|
||||||
self.save_button.clicked.connect(self.save_settings)
|
self.save_button.clicked.connect(self.save_settings)
|
||||||
layout.addWidget(self.save_button)
|
layout.addWidget(self.save_button)
|
||||||
|
|
||||||
@ -431,7 +499,7 @@ class SettingsDialog(QDialog):
|
|||||||
self.parent.save_settings(self.parent.settings)
|
self.parent.save_settings(self.parent.settings)
|
||||||
|
|
||||||
if old_language != language_code:
|
if old_language != language_code:
|
||||||
QMessageBox.information(self, self.parent.translations['restart_required_title'], self.parent.translations['restart_required_message'])
|
QMessageBox.information(self, self.parent.get_translation('restart_required_title', 'Restart Required'), self.parent.get_translation('restart_required_message', 'You need to restart the application for the language changes to take effect.'))
|
||||||
|
|
||||||
self.accept()
|
self.accept()
|
||||||
|
|
||||||
@ -439,4 +507,4 @@ if __name__ == '__main__':
|
|||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
window = URLManager()
|
window = URLManager()
|
||||||
window.show()
|
window.show()
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec())
|
||||||
|
|||||||
369
app.py
369
app.py
@ -1,15 +1,64 @@
|
|||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
|
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
|
||||||
QLineEdit, QPushButton, QTextEdit, QComboBox,
|
QLineEdit, QPushButton, QTextEdit, QMessageBox,
|
||||||
QMessageBox, QSpacerItem, QSizePolicy, QLabel, QFileDialog, QDialog, QFormLayout, QTableWidget, QTableWidgetItem,
|
QSpacerItem, QSizePolicy, QLabel, QFileDialog, QDialog,
|
||||||
QHeaderView
|
QFormLayout, QTableWidget, QTableWidgetItem, QHeaderView,
|
||||||
|
QComboBox, QAbstractItemView
|
||||||
)
|
)
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt6.QtGui import QIcon, QDesktopServices
|
||||||
from PyQt5.QtCore import Qt, QDateTime
|
from PyQt6.QtCore import Qt, QDateTime, QUrl
|
||||||
from PyQt5.QtSvg import QSvgWidget
|
from PyQt6.QtSvgWidgets import QSvgWidget
|
||||||
|
|
||||||
|
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:
|
||||||
|
QDesktopServices.openUrl(QUrl(self.url)) # Open the URL directly if no valid search term is extracted
|
||||||
|
|
||||||
class URLManager(QWidget):
|
class URLManager(QWidget):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -18,8 +67,8 @@ class URLManager(QWidget):
|
|||||||
self.settings = self.load_settings()
|
self.settings = self.load_settings()
|
||||||
self.translations = {}
|
self.translations = {}
|
||||||
self.load_translations(f'lang/translations_{self.settings["language"].lower()[:2]}.json')
|
self.load_translations(f'lang/translations_{self.settings["language"].lower()[:2]}.json')
|
||||||
self.setWindowTitle(self.translations['title'])
|
self.setWindowTitle(self.get_translation('title', 'URL Manager'))
|
||||||
self.setGeometry(100, 100, 600, 800)
|
self.setGeometry(100, 100, 600, 600)
|
||||||
self.setWindowIcon(QIcon('assets/logo.png'))
|
self.setWindowIcon(QIcon('assets/logo.png'))
|
||||||
|
|
||||||
self.layout = QVBoxLayout()
|
self.layout = QVBoxLayout()
|
||||||
@ -27,16 +76,18 @@ class URLManager(QWidget):
|
|||||||
# URL input and settings button layout
|
# URL input and settings button layout
|
||||||
self.url_layout = QHBoxLayout()
|
self.url_layout = QHBoxLayout()
|
||||||
self.url_input = QLineEdit()
|
self.url_input = QLineEdit()
|
||||||
self.url_input.setPlaceholderText(self.translations['url_placeholder'])
|
self.url_input.setPlaceholderText(self.get_translation('url_placeholder', 'Enter URL'))
|
||||||
self.url_layout.addWidget(self.url_input)
|
self.url_layout.addWidget(self.url_input)
|
||||||
|
|
||||||
self.settings_button = QPushButton()
|
self.settings_button = QPushButton()
|
||||||
self.settings_button.setFixedSize(24, 24)
|
self.settings_button.setFixedSize(28, 28)
|
||||||
|
self.settings_button.setStyleSheet("padding: 5px;")
|
||||||
self.settings_button.clicked.connect(self.show_settings_dialog)
|
self.settings_button.clicked.connect(self.show_settings_dialog)
|
||||||
self.url_layout.addWidget(self.settings_button)
|
self.url_layout.addWidget(self.settings_button)
|
||||||
|
|
||||||
self.info_button = QPushButton()
|
self.info_button = QPushButton()
|
||||||
self.info_button.setFixedSize(24, 24)
|
self.info_button.setFixedSize(28, 28)
|
||||||
|
self.info_button.setStyleSheet("padding: 5px;")
|
||||||
self.info_button.clicked.connect(self.show_info_dialog)
|
self.info_button.clicked.connect(self.show_info_dialog)
|
||||||
self.url_layout.addWidget(self.info_button)
|
self.url_layout.addWidget(self.info_button)
|
||||||
|
|
||||||
@ -45,59 +96,42 @@ class URLManager(QWidget):
|
|||||||
self.update_icon_color()
|
self.update_icon_color()
|
||||||
|
|
||||||
self.description_input = QTextEdit()
|
self.description_input = QTextEdit()
|
||||||
self.description_input.setPlaceholderText(self.translations['description_placeholder'])
|
self.description_input.setPlaceholderText(self.get_translation('description_placeholder', 'Enter Description or Thoughts'))
|
||||||
self.description_input.setMaximumHeight(100)
|
self.description_input.setMaximumHeight(100)
|
||||||
self.layout.addWidget(self.description_input)
|
self.layout.addWidget(self.description_input)
|
||||||
|
|
||||||
self.group_layout = QHBoxLayout()
|
self.add_button = QPushButton(self.get_translation('save_url_button', 'Save URL'))
|
||||||
|
|
||||||
self.group_combobox = QComboBox()
|
|
||||||
self.group_combobox.addItem(self.translations['all_categories'])
|
|
||||||
self.group_combobox.addItem(self.translations['default_category'])
|
|
||||||
self.group_combobox.currentTextChanged.connect(self.filter_urls_by_category)
|
|
||||||
self.group_layout.addWidget(self.group_combobox)
|
|
||||||
|
|
||||||
self.category_input = QLineEdit()
|
|
||||||
self.category_input.setPlaceholderText(self.translations['category_name'])
|
|
||||||
self.category_input.setFixedWidth(3 * 100) # Fixed width for the text input box, assuming "Save" button width is 100
|
|
||||||
self.group_layout.addWidget(self.category_input)
|
|
||||||
|
|
||||||
self.save_category_button = QPushButton(self.translations['save_category_button'])
|
|
||||||
self.save_category_button.setFixedWidth(100) # Fixed width for the "Save" button
|
|
||||||
self.save_category_button.clicked.connect(self.save_category)
|
|
||||||
self.group_layout.addWidget(self.save_category_button)
|
|
||||||
|
|
||||||
self.layout.addLayout(self.group_layout)
|
|
||||||
|
|
||||||
self.add_button = QPushButton(self.translations['save_url_button'])
|
|
||||||
self.add_button.clicked.connect(self.add_url)
|
self.add_button.clicked.connect(self.add_url)
|
||||||
self.layout.addWidget(self.add_button)
|
self.layout.addWidget(self.add_button)
|
||||||
|
|
||||||
self.layout.addSpacerItem(QSpacerItem(0, 10, QSizePolicy.Minimum, QSizePolicy.Fixed))
|
self.layout.addSpacerItem(QSpacerItem(0, 10, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed))
|
||||||
|
|
||||||
self.search_layout = QHBoxLayout()
|
self.search_layout = QHBoxLayout()
|
||||||
self.search_input = QLineEdit()
|
self.search_input = QLineEdit()
|
||||||
self.search_input.setPlaceholderText(self.translations['search_placeholder'])
|
self.search_input.setPlaceholderText(self.get_translation('search_placeholder', 'Search...'))
|
||||||
self.search_input.textChanged.connect(self.filter_urls)
|
self.search_input.textChanged.connect(self.filter_urls)
|
||||||
self.search_layout.addWidget(self.search_input)
|
self.search_layout.addWidget(self.search_input)
|
||||||
self.layout.addLayout(self.search_layout)
|
self.layout.addLayout(self.search_layout)
|
||||||
|
|
||||||
self.search_input.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
self.search_input.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||||
|
|
||||||
self.url_list = QTableWidget()
|
self.url_list = QTableWidget()
|
||||||
self.url_list.setColumnCount(3)
|
self.url_list.setColumnCount(3)
|
||||||
self.url_list.setHorizontalHeaderLabels(['URL', self.translations['column_date'], self.translations['column_actions']])
|
self.url_list.setHorizontalHeaderLabels([
|
||||||
self.url_list.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
|
self.get_translation('column_url', 'URL'),
|
||||||
|
self.get_translation('column_date', 'Date'),
|
||||||
|
self.get_translation('column_actions', 'Actions')
|
||||||
|
])
|
||||||
|
self.url_list.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
|
||||||
self.url_list.setColumnWidth(1, 150)
|
self.url_list.setColumnWidth(1, 150)
|
||||||
self.url_list.setColumnWidth(2, 100)
|
self.url_list.setColumnWidth(2, 100)
|
||||||
|
self.url_list.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
|
||||||
self.layout.addWidget(self.url_list)
|
self.layout.addWidget(self.url_list)
|
||||||
|
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
self.urls = []
|
self.urls = []
|
||||||
self.groups = set()
|
|
||||||
|
|
||||||
self.load_categories()
|
|
||||||
self.load_urls()
|
self.load_urls()
|
||||||
|
|
||||||
self.search_engines = {
|
self.search_engines = {
|
||||||
@ -116,23 +150,28 @@ class URLManager(QWidget):
|
|||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
self.translations = data['translations']
|
self.translations = data['translations']
|
||||||
|
|
||||||
|
def get_translation(self, key, default):
|
||||||
|
return self.translations.get(key, default)
|
||||||
|
|
||||||
def update_icon_color(self):
|
def update_icon_color(self):
|
||||||
palette = self.palette()
|
palette = self.palette()
|
||||||
if palette.color(palette.Window).value() < 128:
|
if palette.color(palette.ColorRole.Window).value() < 128:
|
||||||
settings_icon_path = 'assets/cogwheel_dark.svg'
|
settings_icon_path = 'assets/cogwheel_dark.svg'
|
||||||
info_icon_path = 'assets/info_dark.svg'
|
info_icon_path = 'assets/info_dark.svg'
|
||||||
self.action_icons = {
|
self.action_icons = {
|
||||||
"edit": 'assets/edit_dark.svg',
|
"visit": 'assets/visit_dark.svg',
|
||||||
"delete": 'assets/delete_dark.svg',
|
"info": 'assets/info_dark.svg',
|
||||||
"search": 'assets/search_dark.svg'
|
"search": 'assets/search_dark.svg',
|
||||||
|
"delete": 'assets/delete_dark.svg'
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
settings_icon_path = 'assets/cogwheel_light.svg'
|
settings_icon_path = 'assets/cogwheel_light.svg'
|
||||||
info_icon_path = 'assets/info_light.svg'
|
info_icon_path = 'assets/info_light.svg'
|
||||||
self.action_icons = {
|
self.action_icons = {
|
||||||
"edit": 'assets/edit_light.svg',
|
"visit": 'assets/visit_light.svg',
|
||||||
"delete": 'assets/delete_light.svg',
|
"info": 'assets/info_light.svg',
|
||||||
"search": 'assets/search_light.svg'
|
"search": 'assets/search_light.svg',
|
||||||
|
"delete": 'assets/delete_light.svg'
|
||||||
}
|
}
|
||||||
|
|
||||||
self.settings_button.setIcon(QIcon(settings_icon_path))
|
self.settings_button.setIcon(QIcon(settings_icon_path))
|
||||||
@ -141,10 +180,9 @@ class URLManager(QWidget):
|
|||||||
def add_url(self):
|
def add_url(self):
|
||||||
url = self.url_input.text()
|
url = self.url_input.text()
|
||||||
description = self.description_input.toPlainText()
|
description = self.description_input.toPlainText()
|
||||||
group = self.group_combobox.currentText()
|
|
||||||
if url:
|
if url:
|
||||||
date_added = QDateTime.currentDateTime().toString(Qt.ISODate)
|
date_added = QDateTime.currentDateTime().toString(Qt.DateFormat.ISODate)
|
||||||
url_data = {'url': url, 'description': description, 'date': date_added, 'group': group}
|
url_data = {'url': url, 'description': description, 'date': date_added}
|
||||||
self.urls.append(url_data)
|
self.urls.append(url_data)
|
||||||
self.save_url(url_data)
|
self.save_url(url_data)
|
||||||
self.update_url_list()
|
self.update_url_list()
|
||||||
@ -152,35 +190,26 @@ class URLManager(QWidget):
|
|||||||
self.description_input.clear()
|
self.description_input.clear()
|
||||||
|
|
||||||
def save_url(self, url_data):
|
def save_url(self, url_data):
|
||||||
category = url_data['group']
|
url_path = os.path.join(self.settings['data_directory'], 'urls.json')
|
||||||
category_filename = self.get_category_filename(category)
|
if os.path.exists(url_path):
|
||||||
category_path = os.path.join(self.settings['data_directory'], category_filename)
|
with open(url_path, 'r') as file:
|
||||||
if os.path.exists(category_path):
|
url_list = json.load(file)
|
||||||
with open(category_path, 'r') as file:
|
|
||||||
category_urls = json.load(file)
|
|
||||||
else:
|
else:
|
||||||
category_urls = []
|
url_list = []
|
||||||
category_urls.append(url_data)
|
url_list.append(url_data)
|
||||||
with open(category_path, 'w') as file:
|
with open(url_path, 'w') as file:
|
||||||
json.dump(category_urls, file)
|
json.dump(url_list, file)
|
||||||
|
|
||||||
def update_url(self):
|
def update_url(self, row, new_url, new_description):
|
||||||
selected_item = self.url_list.currentItem()
|
self.urls[row]['url'] = new_url
|
||||||
if selected_item:
|
self.urls[row]['description'] = new_description
|
||||||
row = selected_item.row()
|
self.save_urls()
|
||||||
new_url = self.url_input.text()
|
self.update_url_list()
|
||||||
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):
|
def delete_url(self, row):
|
||||||
reply = QMessageBox.question(self, self.translations['delete_confirmation_title'], self.translations['delete_confirmation'],
|
reply = QMessageBox.question(self, self.get_translation('delete_confirmation_title', 'Delete URL'), self.get_translation('delete_confirmation', 'Are you sure you want to delete this URL?'),
|
||||||
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No)
|
||||||
if reply == QMessageBox.Yes:
|
if reply == QMessageBox.StandardButton.Yes:
|
||||||
del self.urls[row]
|
del self.urls[row]
|
||||||
self.save_urls()
|
self.save_urls()
|
||||||
self.update_url_list()
|
self.update_url_list()
|
||||||
@ -194,14 +223,6 @@ class URLManager(QWidget):
|
|||||||
filtered_urls = [url for url in self.urls if search_term in url['url'].lower() or search_term in url['description'].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)
|
self.update_url_list(filtered_urls)
|
||||||
|
|
||||||
def filter_urls_by_category(self):
|
|
||||||
selected_category = self.group_combobox.currentText()
|
|
||||||
if selected_category == self.translations['all_categories']:
|
|
||||||
self.update_url_list(self.urls)
|
|
||||||
else:
|
|
||||||
filtered_urls = [url for url in self.urls if url['group'] == selected_category]
|
|
||||||
self.update_url_list(filtered_urls)
|
|
||||||
|
|
||||||
def update_url_list(self, urls=None):
|
def update_url_list(self, urls=None):
|
||||||
self.url_list.setRowCount(0)
|
self.url_list.setRowCount(0)
|
||||||
urls_to_display = urls if urls else self.urls
|
urls_to_display = urls if urls else self.urls
|
||||||
@ -209,21 +230,39 @@ class URLManager(QWidget):
|
|||||||
self.url_list.insertRow(row)
|
self.url_list.insertRow(row)
|
||||||
self.url_list.setItem(row, 0, QTableWidgetItem(url['url']))
|
self.url_list.setItem(row, 0, QTableWidgetItem(url['url']))
|
||||||
formatted_date = self.format_date(url['date'])
|
formatted_date = self.format_date(url['date'])
|
||||||
self.url_list.setItem(row, 1, QTableWidgetItem(formatted_date))
|
date_item = QTableWidgetItem(formatted_date)
|
||||||
|
date_item.setTextAlignment(Qt.AlignmentFlag.AlignVCenter)
|
||||||
|
self.url_list.setItem(row, 1, date_item)
|
||||||
|
|
||||||
actions_layout = QHBoxLayout()
|
actions_layout = QHBoxLayout()
|
||||||
|
actions_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
actions_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
edit_button = QPushButton()
|
visit_button = QPushButton()
|
||||||
edit_button.setIcon(QIcon(self.action_icons["edit"]))
|
visit_button.setIcon(QIcon(self.action_icons["visit"]))
|
||||||
edit_button.setFixedSize(18, 18)
|
visit_button.setFixedSize(22, 22)
|
||||||
edit_button.setStyleSheet("border: none; padding: 0px;")
|
visit_button.setStyleSheet("border: none; padding: 3px;")
|
||||||
edit_button.clicked.connect(lambda _, row=row: self.edit_url(row))
|
visit_button.clicked.connect(lambda _, url=url['url']: self.visit_url(url))
|
||||||
actions_layout.addWidget(edit_button)
|
actions_layout.addWidget(visit_button)
|
||||||
|
|
||||||
|
info_button = QPushButton()
|
||||||
|
info_button.setIcon(QIcon(self.action_icons["info"]))
|
||||||
|
info_button.setFixedSize(22, 22)
|
||||||
|
info_button.setStyleSheet("border: none; padding: 3px;")
|
||||||
|
info_button.clicked.connect(lambda _, row=row: self.view_description(row))
|
||||||
|
actions_layout.addWidget(info_button)
|
||||||
|
|
||||||
|
search_button = QPushButton()
|
||||||
|
search_button.setIcon(QIcon(self.action_icons["search"]))
|
||||||
|
search_button.setFixedSize(22, 22)
|
||||||
|
search_button.setStyleSheet("border: none; padding: 3px;")
|
||||||
|
search_button.clicked.connect(lambda _, url=url['url']: self.search_url(url))
|
||||||
|
actions_layout.addWidget(search_button)
|
||||||
|
|
||||||
delete_button = QPushButton()
|
delete_button = QPushButton()
|
||||||
delete_button.setIcon(QIcon(self.action_icons["delete"]))
|
delete_button.setIcon(QIcon(self.action_icons["delete"]))
|
||||||
delete_button.setFixedSize(18, 18)
|
delete_button.setFixedSize(22, 22)
|
||||||
delete_button.setStyleSheet("border: none; padding: 0px;")
|
delete_button.setStyleSheet("border: none; padding: 3px;")
|
||||||
delete_button.clicked.connect(lambda _, row=row: self.delete_url(row))
|
delete_button.clicked.connect(lambda _, row=row: self.delete_url(row))
|
||||||
actions_layout.addWidget(delete_button)
|
actions_layout.addWidget(delete_button)
|
||||||
|
|
||||||
@ -236,7 +275,7 @@ class URLManager(QWidget):
|
|||||||
vertical_header.setVisible(False)
|
vertical_header.setVisible(False)
|
||||||
|
|
||||||
def format_date(self, date_str):
|
def format_date(self, date_str):
|
||||||
date = QDateTime.fromString(date_str, Qt.ISODate)
|
date = QDateTime.fromString(date_str, Qt.DateFormat.ISODate)
|
||||||
if self.settings['date_format'] == 'Nerdy':
|
if self.settings['date_format'] == 'Nerdy':
|
||||||
return date.toString("yyyy-MM-ddTHH:mm:ss")
|
return date.toString("yyyy-MM-ddTHH:mm:ss")
|
||||||
elif self.settings['date_format'] == 'Normal':
|
elif self.settings['date_format'] == 'Normal':
|
||||||
@ -246,11 +285,17 @@ class URLManager(QWidget):
|
|||||||
else:
|
else:
|
||||||
return date_str
|
return date_str
|
||||||
|
|
||||||
def edit_url(self, row):
|
def visit_url(self, url):
|
||||||
url = self.urls[row]['url']
|
QDesktopServices.openUrl(QUrl(url))
|
||||||
description = self.urls[row]['description']
|
|
||||||
self.url_input.setText(url)
|
def view_description(self, row):
|
||||||
self.description_input.setPlainText(description)
|
dialog = DescriptionDialog(self, row)
|
||||||
|
dialog.exec()
|
||||||
|
|
||||||
|
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):
|
def load_settings(self):
|
||||||
settings_path = 'data/settings.json'
|
settings_path = 'data/settings.json'
|
||||||
@ -285,109 +330,99 @@ class URLManager(QWidget):
|
|||||||
with open(settings_path, 'w') as file:
|
with open(settings_path, 'w') as file:
|
||||||
json.dump(settings, file)
|
json.dump(settings, file)
|
||||||
|
|
||||||
def load_categories(self):
|
|
||||||
self.groups = set()
|
|
||||||
categories_path = os.path.join(self.settings['data_directory'], 'categories.json')
|
|
||||||
if os.path.exists(categories_path):
|
|
||||||
with open(categories_path, 'r') as file:
|
|
||||||
self.groups = set(json.load(file))
|
|
||||||
self.update_group_combobox()
|
|
||||||
self.group_combobox.setCurrentText(self.translations['default_category'])
|
|
||||||
|
|
||||||
def save_categories(self):
|
|
||||||
categories_path = os.path.join(self.settings['data_directory'], 'categories.json')
|
|
||||||
os.makedirs(os.path.dirname(categories_path), exist_ok=True)
|
|
||||||
with open(categories_path, 'w') as file:
|
|
||||||
json.dump(list(self.groups), file)
|
|
||||||
|
|
||||||
def update_group_combobox(self):
|
|
||||||
self.group_combobox.clear()
|
|
||||||
self.group_combobox.addItem(self.translations['all_categories'])
|
|
||||||
self.group_combobox.addItem(self.translations['default_category'])
|
|
||||||
for group in sorted(self.groups):
|
|
||||||
self.group_combobox.addItem(group)
|
|
||||||
|
|
||||||
def save_category(self):
|
|
||||||
new_category = self.category_input.text().strip()
|
|
||||||
if new_category and new_category not in self.groups:
|
|
||||||
self.groups.add(new_category)
|
|
||||||
self.save_categories()
|
|
||||||
self.update_group_combobox()
|
|
||||||
self.category_input.clear()
|
|
||||||
|
|
||||||
def load_urls(self):
|
def load_urls(self):
|
||||||
self.urls = []
|
url_path = os.path.join(self.settings['data_directory'], 'urls.json')
|
||||||
for group in self.groups:
|
if os.path.exists(url_path):
|
||||||
category_filename = self.get_category_filename(group)
|
with open(url_path, 'r') as file:
|
||||||
category_path = os.path.join(self.settings['data_directory'], category_filename)
|
self.urls = json.load(file)
|
||||||
if os.path.exists(category_path):
|
|
||||||
with open(category_path, 'r') as file:
|
|
||||||
self.urls.extend(json.load(file))
|
|
||||||
self.update_url_list()
|
self.update_url_list()
|
||||||
|
|
||||||
def save_urls(self):
|
def save_urls(self):
|
||||||
for group in self.groups:
|
url_path = os.path.join(self.settings['data_directory'], 'urls.json')
|
||||||
group_urls = [url for url in self.urls if url['group'] == group]
|
with open(url_path, 'w') as file:
|
||||||
category_filename = self.get_category_filename(group)
|
json.dump(self.urls, file)
|
||||||
category_path = os.path.join(self.settings['data_directory'], category_filename)
|
|
||||||
with open(category_path, 'w') as file:
|
|
||||||
json.dump(group_urls, file)
|
|
||||||
|
|
||||||
def get_category_filename(self, category):
|
|
||||||
if ' ' in category:
|
|
||||||
return category.replace(' ', '_').lower() + '.json'
|
|
||||||
else:
|
|
||||||
return category.lower() + '.json'
|
|
||||||
|
|
||||||
def show_info_dialog(self):
|
def show_info_dialog(self):
|
||||||
dialog = QDialog(self)
|
dialog = QDialog(self)
|
||||||
dialog.setWindowTitle(self.translations['about_title'])
|
dialog.setWindowTitle(self.get_translation('about_title', 'About URL Manager'))
|
||||||
|
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
svg_widget = QSvgWidget('assets/logo.svg')
|
svg_widget = QSvgWidget('assets/logo.svg')
|
||||||
svg_widget.setFixedSize(256, 256)
|
svg_widget.setFixedSize(256, 256)
|
||||||
layout.addWidget(svg_widget, alignment=Qt.AlignCenter)
|
layout.addWidget(svg_widget, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
written_by_label = QLabel(self.translations['developed_by'])
|
written_by_label = QLabel(self.get_translation('developed_by', 'Developed by Axel Rafn'))
|
||||||
layout.addWidget(written_by_label, alignment=Qt.AlignCenter)
|
layout.addWidget(written_by_label, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
link_label = QLabel()
|
link_label = QLabel()
|
||||||
link_label.setText(f'<a href="{self.translations["repository_link"]}">{self.translations["repository_link_name"]}</a>')
|
link_label.setText(f'<a href="{self.get_translation("repository_link", "#")}">{self.get_translation("repository_link_name", "Project Home")}</a>')
|
||||||
link_label.setOpenExternalLinks(True)
|
link_label.setOpenExternalLinks(True)
|
||||||
layout.addWidget(link_label, alignment=Qt.AlignCenter)
|
layout.addWidget(link_label, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
dialog.setLayout(layout)
|
dialog.setLayout(layout)
|
||||||
dialog.exec_()
|
dialog.exec()
|
||||||
|
|
||||||
def show_settings_dialog(self):
|
def show_settings_dialog(self):
|
||||||
dialog = SettingsDialog(self)
|
dialog = SettingsDialog(self)
|
||||||
if dialog.exec_() == QDialog.Accepted:
|
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||||
self.load_settings()
|
self.settings = self.load_settings()
|
||||||
self.update_url_list()
|
self.update_url_list()
|
||||||
|
|
||||||
|
class DescriptionDialog(QDialog):
|
||||||
|
def __init__(self, parent, row):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.parent = parent
|
||||||
|
self.row = row
|
||||||
|
self.setWindowTitle(self.parent.get_translation('description_title', 'URL Description'))
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
self.url_input = QLineEdit(self.parent.urls[row]['url'])
|
||||||
|
layout.addWidget(self.url_input)
|
||||||
|
|
||||||
|
self.description_input = QTextEdit(self.parent.urls[row]['description'])
|
||||||
|
layout.addWidget(self.description_input)
|
||||||
|
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
self.update_button = QPushButton(self.parent.get_translation('update_button', 'Update'))
|
||||||
|
self.update_button.clicked.connect(self.update_url)
|
||||||
|
self.close_button = QPushButton(self.parent.get_translation('close_button', 'Close'))
|
||||||
|
self.close_button.clicked.connect(self.close)
|
||||||
|
button_layout.addWidget(self.update_button)
|
||||||
|
button_layout.addWidget(self.close_button)
|
||||||
|
layout.addLayout(button_layout)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def update_url(self):
|
||||||
|
new_url = self.url_input.text()
|
||||||
|
new_description = self.description_input.toPlainText()
|
||||||
|
self.parent.update_url(self.row, new_url, new_description)
|
||||||
|
self.accept()
|
||||||
|
|
||||||
class SettingsDialog(QDialog):
|
class SettingsDialog(QDialog):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setWindowTitle(parent.translations['settings_title'])
|
self.setWindowTitle(parent.get_translation('settings_title', 'Settings'))
|
||||||
self.setFixedWidth(360)
|
self.setFixedWidth(360)
|
||||||
|
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
directory_label = QLabel(parent.translations['data_directory_label'])
|
directory_label = QLabel(parent.get_translation('data_directory_label', 'Data Directory:'))
|
||||||
layout.addWidget(directory_label)
|
layout.addWidget(directory_label)
|
||||||
|
|
||||||
directory_layout = QHBoxLayout()
|
directory_layout = QHBoxLayout()
|
||||||
self.directory_input = QLineEdit(self.parent.settings['data_directory'])
|
self.directory_input = QLineEdit(self.parent.settings['data_directory'])
|
||||||
self.browse_button = QPushButton(self.parent.translations['browse_button'])
|
self.browse_button = QPushButton(self.parent.get_translation('browse_button', 'Browse...'))
|
||||||
self.browse_button.clicked.connect(self.browse_directory)
|
self.browse_button.clicked.connect(self.browse_directory)
|
||||||
directory_layout.addWidget(self.directory_input)
|
directory_layout.addWidget(self.directory_input)
|
||||||
directory_layout.addWidget(self.browse_button)
|
directory_layout.addWidget(self.browse_button)
|
||||||
layout.addLayout(directory_layout)
|
layout.addLayout(directory_layout)
|
||||||
|
|
||||||
layout.addSpacerItem(QSpacerItem(0, 4, QSizePolicy.Minimum, QSizePolicy.Fixed))
|
layout.addSpacerItem(QSpacerItem(0, 4, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed))
|
||||||
|
|
||||||
form_layout = QFormLayout()
|
form_layout = QFormLayout()
|
||||||
|
|
||||||
@ -396,24 +431,24 @@ class SettingsDialog(QDialog):
|
|||||||
search_engines = sorted(self.parent.search_engines.keys())
|
search_engines = sorted(self.parent.search_engines.keys())
|
||||||
self.search_engine_combobox.addItems(search_engines)
|
self.search_engine_combobox.addItems(search_engines)
|
||||||
self.search_engine_combobox.setCurrentText(self.parent.settings['search_engine'])
|
self.search_engine_combobox.setCurrentText(self.parent.settings['search_engine'])
|
||||||
form_layout.addRow(self.parent.translations['search_engine_label'], self.search_engine_combobox)
|
form_layout.addRow(self.parent.get_translation('search_engine_label', 'Search Engine:'), self.search_engine_combobox)
|
||||||
|
|
||||||
# Populate Date Format Combobox
|
# Populate Date Format Combobox
|
||||||
self.date_format_combobox = QComboBox()
|
self.date_format_combobox = QComboBox()
|
||||||
self.date_format_combobox.addItems(["Nerdy", "Normal", "Murica!"])
|
self.date_format_combobox.addItems(["Nerdy", "Normal", "Murica!"])
|
||||||
self.date_format_combobox.setCurrentText(self.parent.settings['date_format'])
|
self.date_format_combobox.setCurrentText(self.parent.settings['date_format'])
|
||||||
form_layout.addRow(self.parent.translations['date_format_label'], self.date_format_combobox)
|
form_layout.addRow(self.parent.get_translation('date_format_label', 'Date Format:'), self.date_format_combobox)
|
||||||
|
|
||||||
# Populate Language Combobox
|
# Populate Language Combobox
|
||||||
self.language_combobox = QComboBox()
|
self.language_combobox = QComboBox()
|
||||||
self.populate_language_combobox()
|
self.populate_language_combobox()
|
||||||
current_language_code = self.parent.settings['language']
|
current_language_code = self.parent.settings['language']
|
||||||
self.set_current_language(current_language_code)
|
self.set_current_language(current_language_code)
|
||||||
form_layout.addRow(self.parent.translations['language_label'], self.language_combobox)
|
form_layout.addRow(self.parent.get_translation('language_label', 'Language:'), self.language_combobox)
|
||||||
|
|
||||||
layout.addLayout(form_layout)
|
layout.addLayout(form_layout)
|
||||||
|
|
||||||
self.save_button = QPushButton(self.parent.translations['save_button'])
|
self.save_button = QPushButton(self.parent.get_translation('save_button', 'Save'))
|
||||||
self.save_button.clicked.connect(self.save_settings)
|
self.save_button.clicked.connect(self.save_settings)
|
||||||
layout.addWidget(self.save_button)
|
layout.addWidget(self.save_button)
|
||||||
|
|
||||||
@ -457,7 +492,7 @@ class SettingsDialog(QDialog):
|
|||||||
self.parent.save_settings(self.parent.settings)
|
self.parent.save_settings(self.parent.settings)
|
||||||
|
|
||||||
if old_language != language_code:
|
if old_language != language_code:
|
||||||
QMessageBox.information(self, self.parent.translations['restart_required_title'], self.parent.translations['restart_required_message'])
|
QMessageBox.information(self, self.parent.get_translation('restart_required_title', 'Restart Required'), self.parent.get_translation('restart_required_message', 'You need to restart the application for the language changes to take effect.'))
|
||||||
|
|
||||||
self.accept()
|
self.accept()
|
||||||
|
|
||||||
@ -465,4 +500,4 @@ if __name__ == '__main__':
|
|||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
window = URLManager()
|
window = URLManager()
|
||||||
window.show()
|
window.show()
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec())
|
||||||
|
|||||||
@ -26,7 +26,11 @@
|
|||||||
"language_label": "Language:",
|
"language_label": "Language:",
|
||||||
"restart_required_title": "Restart Required",
|
"restart_required_title": "Restart Required",
|
||||||
"restart_required_message": "You need to restart the application for the language changes to take effect.",
|
"restart_required_message": "You need to restart the application for the language changes to take effect.",
|
||||||
|
"column_url": "URL",
|
||||||
"column_date": "Date",
|
"column_date": "Date",
|
||||||
"column_actions": "Actions"
|
"column_actions": "Actions",
|
||||||
|
"description_title": "URL Description",
|
||||||
|
"update_button": "Update",
|
||||||
|
"close_button": "Close"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,8 +11,8 @@
|
|||||||
"save_category_button": "Vista Flokk",
|
"save_category_button": "Vista Flokk",
|
||||||
"save_url_button": "Vista Slóð",
|
"save_url_button": "Vista Slóð",
|
||||||
"search_placeholder": "Leita..",
|
"search_placeholder": "Leita..",
|
||||||
"delete_confirmation": "Ertu viss um að þú viljir eyða þessu URLi?",
|
"delete_confirmation": "Ertu viss um að þú viljir eyða þessari slóð?",
|
||||||
"delete_confirmation_title": "Eyða URLi",
|
"delete_confirmation_title": "Eyða Slóð",
|
||||||
"settings_title": "Stillingar",
|
"settings_title": "Stillingar",
|
||||||
"about_title": "Um tabRemember",
|
"about_title": "Um tabRemember",
|
||||||
"developed_by": "Þróað af Axel Rafn",
|
"developed_by": "Þróað af Axel Rafn",
|
||||||
@ -24,9 +24,13 @@
|
|||||||
"date_format_label": "Dagsetningar form:",
|
"date_format_label": "Dagsetningar form:",
|
||||||
"save_button": "Vista",
|
"save_button": "Vista",
|
||||||
"language_label": "Tungumál:",
|
"language_label": "Tungumál:",
|
||||||
"restart_required_title": "Endurræsingu krafist",
|
"restart_required_title": "Þarfnast endurræsingar",
|
||||||
"restart_required_message": "Þú þarft að endurræsa forritið til að breytingar á tungumáli taki gildi.",
|
"restart_required_message": "Þú þarft að endurræsa forritið til að breytingar á tungumáli taki gildi.",
|
||||||
|
"column_url": "Slóð",
|
||||||
"column_date": "Dags",
|
"column_date": "Dags",
|
||||||
"column_actions": "Aðgerðir"
|
"column_actions": "Aðgerðir",
|
||||||
|
"description_title": "Lýsing á Slóð",
|
||||||
|
"update_button": "Uppfæra",
|
||||||
|
"close_button": "Loka"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1 +1 @@
|
|||||||
PyQT5
|
PyQT6
|
||||||
|
|||||||
196
style-new.qss
Normal file
196
style-new.qss
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
/* General application styling */
|
||||||
|
QWidget {
|
||||||
|
color: rgb(221, 221, 221);
|
||||||
|
font: 11pt "Segoe UI";
|
||||||
|
background-color: rgb(40, 44, 52);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* QLineEdit styling */
|
||||||
|
QLineEdit {
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
border: 2px solid rgb(33, 37, 43);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding-left: 10px;
|
||||||
|
selection-color: rgb(255, 255, 255);
|
||||||
|
selection-background-color: rgb(189, 147, 249);
|
||||||
|
}
|
||||||
|
QLineEdit:hover {
|
||||||
|
border: 2px solid rgb(64, 71, 88);
|
||||||
|
}
|
||||||
|
QLineEdit:focus {
|
||||||
|
border: 2px solid rgb(91, 101, 124);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* QTextEdit styling */
|
||||||
|
QTextEdit {
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
border: 2px solid rgb(33, 37, 43);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
selection-color: rgb(255, 255, 255);
|
||||||
|
selection-background-color: rgb(189, 147, 249);
|
||||||
|
}
|
||||||
|
QTextEdit:hover {
|
||||||
|
border: 2px solid rgb(64, 71, 88);
|
||||||
|
}
|
||||||
|
QTextEdit:focus {
|
||||||
|
border: 2px solid rgb(91, 101, 124);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* QPushButton styling */
|
||||||
|
QPushButton {
|
||||||
|
background-color: rgb(52, 59, 72);
|
||||||
|
border: 2px solid rgb(52, 59, 72);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
color: rgb(221, 221, 221);
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: rgb(57, 65, 80);
|
||||||
|
border: 2px solid rgb(61, 70, 86);
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: rgb(35, 40, 49);
|
||||||
|
border: 2px solid rgb(43, 50, 61);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* QTableWidget styling */
|
||||||
|
QTableWidget {
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
border: 2px solid rgb(33, 37, 43);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
gridline-color: rgb(44, 49, 58);
|
||||||
|
}
|
||||||
|
QTableWidget::item {
|
||||||
|
border-color: rgb(44, 49, 60);
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
gridline-color: rgb(44, 49, 60);
|
||||||
|
}
|
||||||
|
QTableWidget::item:selected {
|
||||||
|
background-color: rgb(189, 147, 249);
|
||||||
|
}
|
||||||
|
QHeaderView::section {
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
border: 1px solid rgb(44, 49, 58);
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
QTableWidget::horizontalHeader {
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
}
|
||||||
|
QHeaderView::section:horizontal {
|
||||||
|
border: 1px solid rgb(33, 37, 43);
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
padding: 3px;
|
||||||
|
border-top-left-radius: 7px;
|
||||||
|
border-top-right-radius: 7px;
|
||||||
|
}
|
||||||
|
QHeaderView::section:vertical {
|
||||||
|
border: 1px solid rgb(44, 49, 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* QComboBox styling */
|
||||||
|
QComboBox {
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
border: 2px solid rgb(33, 37, 43);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
QComboBox:hover {
|
||||||
|
border: 2px solid rgb(64, 71, 88);
|
||||||
|
}
|
||||||
|
QComboBox::drop-down {
|
||||||
|
subcontrol-origin: padding;
|
||||||
|
subcontrol-position: top right;
|
||||||
|
width: 25px;
|
||||||
|
border-left-width: 3px;
|
||||||
|
border-left-color: rgba(39, 44, 54, 150);
|
||||||
|
border-left-style: solid;
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
border-bottom-right-radius: 3px;
|
||||||
|
background-image: url(:/icons/images/icons/cil-arrow-bottom.png);
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
QComboBox QAbstractItemView {
|
||||||
|
color: rgb(255, 121, 198);
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
padding: 10px;
|
||||||
|
selection-background-color: rgb(39, 44, 54);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* QScrollBar styling */
|
||||||
|
QScrollBar:horizontal {
|
||||||
|
border: none;
|
||||||
|
background: rgb(52, 59, 72);
|
||||||
|
height: 8px;
|
||||||
|
margin: 0px 21px 0 21px;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
QScrollBar::handle:horizontal {
|
||||||
|
background: rgb(189, 147, 249);
|
||||||
|
min-width: 25px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
QScrollBar::add-line:horizontal {
|
||||||
|
border: none;
|
||||||
|
background: rgb(55, 63, 77);
|
||||||
|
width: 20px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
subcontrol-position: right;
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
}
|
||||||
|
QScrollBar::sub-line:horizontal {
|
||||||
|
border: none;
|
||||||
|
background: rgb(55, 63, 77);
|
||||||
|
width: 20px;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
subcontrol-position: left;
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
}
|
||||||
|
QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
QScrollBar:vertical {
|
||||||
|
border: none;
|
||||||
|
background: rgb(52, 59, 72);
|
||||||
|
width: 8px;
|
||||||
|
margin: 21px 0 21px 0;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
QScrollBar::handle:vertical {
|
||||||
|
background: rgb(189, 147, 249);
|
||||||
|
min-height: 25px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
QScrollBar::add-line:vertical {
|
||||||
|
border: none;
|
||||||
|
background: rgb(55, 63, 77);
|
||||||
|
height: 20px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
subcontrol-position: bottom;
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
}
|
||||||
|
QScrollBar::sub-line:vertical {
|
||||||
|
border: none;
|
||||||
|
background: rgb(55, 63, 77);
|
||||||
|
height: 20px;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
subcontrol-position: top;
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
}
|
||||||
|
QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
500
style.qss
Normal file
500
style.qss
Normal file
@ -0,0 +1,500 @@
|
|||||||
|
|
||||||
|
QWidget{
|
||||||
|
color: rgb(221, 221, 221);
|
||||||
|
font: 10pt "Segoe UI";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Tooltip */
|
||||||
|
QToolTip {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: rgba(33, 37, 43, 180);
|
||||||
|
border: 1px solid rgb(44, 49, 58);
|
||||||
|
background-image: none;
|
||||||
|
background-position: left center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
border: none;
|
||||||
|
border-left: 2px solid rgb(255, 121, 198);
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 8px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Bg App */
|
||||||
|
#bgApp {
|
||||||
|
background-color: rgb(40, 44, 52);
|
||||||
|
border: 1px solid rgb(44, 49, 58);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Left Menu */
|
||||||
|
#leftMenuBg {
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
}
|
||||||
|
#topLogo {
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
background-image: url(:/images/images/images/PyDracula.png);
|
||||||
|
background-position: centered;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
#titleLeftApp { font: 63 12pt "Segoe UI Semibold"; }
|
||||||
|
#titleLeftDescription { font: 8pt "Segoe UI"; color: rgb(189, 147, 249); }
|
||||||
|
|
||||||
|
/* MENUS */
|
||||||
|
#topMenu .QPushButton {
|
||||||
|
background-position: left center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
border: none;
|
||||||
|
border-left: 22px solid transparent;
|
||||||
|
background-color: transparent;
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 44px;
|
||||||
|
}
|
||||||
|
#topMenu .QPushButton:hover {
|
||||||
|
background-color: rgb(40, 44, 52);
|
||||||
|
}
|
||||||
|
#topMenu .QPushButton:pressed {
|
||||||
|
background-color: rgb(189, 147, 249);
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
#bottomMenu .QPushButton {
|
||||||
|
background-position: left center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
border: none;
|
||||||
|
border-left: 20px solid transparent;
|
||||||
|
background-color:transparent;
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 44px;
|
||||||
|
}
|
||||||
|
#bottomMenu .QPushButton:hover {
|
||||||
|
background-color: rgb(40, 44, 52);
|
||||||
|
}
|
||||||
|
#bottomMenu .QPushButton:pressed {
|
||||||
|
background-color: rgb(189, 147, 249);
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
#leftMenuFrame{
|
||||||
|
border-top: 3px solid rgb(44, 49, 58);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toggle Button */
|
||||||
|
#toggleButton {
|
||||||
|
background-position: left center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
border: none;
|
||||||
|
border-left: 20px solid transparent;
|
||||||
|
background-color: rgb(37, 41, 48);
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 44px;
|
||||||
|
color: rgb(113, 126, 149);
|
||||||
|
}
|
||||||
|
#toggleButton:hover {
|
||||||
|
background-color: rgb(40, 44, 52);
|
||||||
|
}
|
||||||
|
#toggleButton:pressed {
|
||||||
|
background-color: rgb(189, 147, 249);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Title Menu */
|
||||||
|
#titleRightInfo { padding-left: 10px; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Extra Tab */
|
||||||
|
#extraLeftBox {
|
||||||
|
background-color: rgb(44, 49, 58);
|
||||||
|
}
|
||||||
|
#extraTopBg{
|
||||||
|
background-color: rgb(189, 147, 249)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon */
|
||||||
|
#extraIcon {
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-image: url(:/icons/images/icons/icon_settings.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Label */
|
||||||
|
#extraLabel { color: rgb(255, 255, 255); }
|
||||||
|
|
||||||
|
/* Btn Close */
|
||||||
|
#extraCloseColumnBtn { background-color: rgba(255, 255, 255, 0); border: none; border-radius: 5px; }
|
||||||
|
#extraCloseColumnBtn:hover { background-color: rgb(196, 161, 249); border-style: solid; border-radius: 4px; }
|
||||||
|
#extraCloseColumnBtn:pressed { background-color: rgb(180, 141, 238); border-style: solid; border-radius: 4px; }
|
||||||
|
|
||||||
|
/* Extra Content */
|
||||||
|
#extraContent{
|
||||||
|
border-top: 3px solid rgb(40, 44, 52);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extra Top Menus */
|
||||||
|
#extraTopMenu .QPushButton {
|
||||||
|
background-position: left center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
border: none;
|
||||||
|
border-left: 22px solid transparent;
|
||||||
|
background-color:transparent;
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 44px;
|
||||||
|
}
|
||||||
|
#extraTopMenu .QPushButton:hover {
|
||||||
|
background-color: rgb(40, 44, 52);
|
||||||
|
}
|
||||||
|
#extraTopMenu .QPushButton:pressed {
|
||||||
|
background-color: rgb(189, 147, 249);
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Content App */
|
||||||
|
#contentTopBg{
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
}
|
||||||
|
#contentBottom{
|
||||||
|
border-top: 3px solid rgb(44, 49, 58);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top Buttons */
|
||||||
|
#rightButtons .QPushButton { background-color: rgba(255, 255, 255, 0); border: none; border-radius: 5px; }
|
||||||
|
#rightButtons .QPushButton:hover { background-color: rgb(44, 49, 57); border-style: solid; border-radius: 4px; }
|
||||||
|
#rightButtons .QPushButton:pressed { background-color: rgb(23, 26, 30); border-style: solid; border-radius: 4px; }
|
||||||
|
|
||||||
|
/* Theme Settings */
|
||||||
|
#extraRightBox { background-color: rgb(44, 49, 58); }
|
||||||
|
#themeSettingsTopDetail { background-color: rgb(189, 147, 249); }
|
||||||
|
|
||||||
|
/* Bottom Bar */
|
||||||
|
#bottomBar { background-color: rgb(44, 49, 58); }
|
||||||
|
#bottomBar QLabel { font-size: 11px; color: rgb(113, 126, 149); padding-left: 10px; padding-right: 10px; padding-bottom: 2px; }
|
||||||
|
|
||||||
|
/* CONTENT SETTINGS */
|
||||||
|
/* MENUS */
|
||||||
|
#contentSettings .QPushButton {
|
||||||
|
background-position: left center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
border: none;
|
||||||
|
border-left: 22px solid transparent;
|
||||||
|
background-color:transparent;
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 44px;
|
||||||
|
}
|
||||||
|
#contentSettings .QPushButton:hover {
|
||||||
|
background-color: rgb(40, 44, 52);
|
||||||
|
}
|
||||||
|
#contentSettings .QPushButton:pressed {
|
||||||
|
background-color: rgb(189, 147, 249);
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QTableWidget */
|
||||||
|
QTableWidget {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
gridline-color: rgb(44, 49, 58);
|
||||||
|
border-bottom: 1px solid rgb(44, 49, 60);
|
||||||
|
}
|
||||||
|
QTableWidget::item{
|
||||||
|
border-color: rgb(44, 49, 60);
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
gridline-color: rgb(44, 49, 60);
|
||||||
|
}
|
||||||
|
QTableWidget::item:selected{
|
||||||
|
background-color: rgb(189, 147, 249);
|
||||||
|
}
|
||||||
|
QHeaderView::section{
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
max-width: 30px;
|
||||||
|
border: 1px solid rgb(44, 49, 58);
|
||||||
|
border-style: none;
|
||||||
|
border-bottom: 1px solid rgb(44, 49, 60);
|
||||||
|
border-right: 1px solid rgb(44, 49, 60);
|
||||||
|
}
|
||||||
|
QTableWidget::horizontalHeader {
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
}
|
||||||
|
QHeaderView::section:horizontal
|
||||||
|
{
|
||||||
|
border: 1px solid rgb(33, 37, 43);
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
padding: 3px;
|
||||||
|
border-top-left-radius: 7px;
|
||||||
|
border-top-right-radius: 7px;
|
||||||
|
}
|
||||||
|
QHeaderView::section:vertical
|
||||||
|
{
|
||||||
|
border: 1px solid rgb(44, 49, 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LineEdit */
|
||||||
|
QLineEdit {
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 2px solid rgb(33, 37, 43);
|
||||||
|
padding-left: 10px;
|
||||||
|
selection-color: rgb(255, 255, 255);
|
||||||
|
selection-background-color: rgb(255, 121, 198);
|
||||||
|
}
|
||||||
|
QLineEdit:hover {
|
||||||
|
border: 2px solid rgb(64, 71, 88);
|
||||||
|
}
|
||||||
|
QLineEdit:focus {
|
||||||
|
border: 2px solid rgb(91, 101, 124);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PlainTextEdit */
|
||||||
|
QPlainTextEdit {
|
||||||
|
background-color: rgb(27, 29, 35);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
selection-color: rgb(255, 255, 255);
|
||||||
|
selection-background-color: rgb(255, 121, 198);
|
||||||
|
}
|
||||||
|
QPlainTextEdit QScrollBar:vertical {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
QPlainTextEdit QScrollBar:horizontal {
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
QPlainTextEdit:hover {
|
||||||
|
border: 2px solid rgb(64, 71, 88);
|
||||||
|
}
|
||||||
|
QPlainTextEdit:focus {
|
||||||
|
border: 2px solid rgb(91, 101, 124);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ScrollBars */
|
||||||
|
QScrollBar:horizontal {
|
||||||
|
border: none;
|
||||||
|
background: rgb(52, 59, 72);
|
||||||
|
height: 8px;
|
||||||
|
margin: 0px 21px 0 21px;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
QScrollBar::handle:horizontal {
|
||||||
|
background: rgb(189, 147, 249);
|
||||||
|
min-width: 25px;
|
||||||
|
border-radius: 4px
|
||||||
|
}
|
||||||
|
QScrollBar::add-line:horizontal {
|
||||||
|
border: none;
|
||||||
|
background: rgb(55, 63, 77);
|
||||||
|
width: 20px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
subcontrol-position: right;
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
}
|
||||||
|
QScrollBar::sub-line:horizontal {
|
||||||
|
border: none;
|
||||||
|
background: rgb(55, 63, 77);
|
||||||
|
width: 20px;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
subcontrol-position: left;
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
}
|
||||||
|
QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal
|
||||||
|
{
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal
|
||||||
|
{
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
QScrollBar:vertical {
|
||||||
|
border: none;
|
||||||
|
background: rgb(52, 59, 72);
|
||||||
|
width: 8px;
|
||||||
|
margin: 21px 0 21px 0;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
QScrollBar::handle:vertical {
|
||||||
|
background: rgb(189, 147, 249);
|
||||||
|
min-height: 25px;
|
||||||
|
border-radius: 4px
|
||||||
|
}
|
||||||
|
QScrollBar::add-line:vertical {
|
||||||
|
border: none;
|
||||||
|
background: rgb(55, 63, 77);
|
||||||
|
height: 20px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
subcontrol-position: bottom;
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
}
|
||||||
|
QScrollBar::sub-line:vertical {
|
||||||
|
border: none;
|
||||||
|
background: rgb(55, 63, 77);
|
||||||
|
height: 20px;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
subcontrol-position: top;
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
}
|
||||||
|
QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CheckBox */
|
||||||
|
QCheckBox::indicator {
|
||||||
|
border: 3px solid rgb(52, 59, 72);
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: rgb(44, 49, 60);
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:hover {
|
||||||
|
border: 3px solid rgb(58, 66, 81);
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:checked {
|
||||||
|
background: 3px solid rgb(52, 59, 72);
|
||||||
|
border: 3px solid rgb(52, 59, 72);
|
||||||
|
background-image: url(:/icons/images/icons/cil-check-alt.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RadioButton */
|
||||||
|
QRadioButton::indicator {
|
||||||
|
border: 3px solid rgb(52, 59, 72);
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: rgb(44, 49, 60);
|
||||||
|
}
|
||||||
|
QRadioButton::indicator:hover {
|
||||||
|
border: 3px solid rgb(58, 66, 81);
|
||||||
|
}
|
||||||
|
QRadioButton::indicator:checked {
|
||||||
|
background: 3px solid rgb(94, 106, 130);
|
||||||
|
border: 3px solid rgb(52, 59, 72);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ComboBox */
|
||||||
|
QComboBox{
|
||||||
|
background-color: rgb(27, 29, 35);
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 2px solid rgb(33, 37, 43);
|
||||||
|
padding: 5px;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
QComboBox:hover{
|
||||||
|
border: 2px solid rgb(64, 71, 88);
|
||||||
|
}
|
||||||
|
QComboBox::drop-down {
|
||||||
|
subcontrol-origin: padding;
|
||||||
|
subcontrol-position: top right;
|
||||||
|
width: 25px;
|
||||||
|
border-left-width: 3px;
|
||||||
|
border-left-color: rgba(39, 44, 54, 150);
|
||||||
|
border-left-style: solid;
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
border-bottom-right-radius: 3px;
|
||||||
|
background-image: url(:/icons/images/icons/cil-arrow-bottom.png);
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-reperat;
|
||||||
|
}
|
||||||
|
QComboBox QAbstractItemView {
|
||||||
|
color: rgb(255, 121, 198);
|
||||||
|
background-color: rgb(33, 37, 43);
|
||||||
|
padding: 10px;
|
||||||
|
selection-background-color: rgb(39, 44, 54);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Sliders */
|
||||||
|
QSlider::groove:horizontal {
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 10px;
|
||||||
|
margin: 0px;
|
||||||
|
background-color: rgb(52, 59, 72);
|
||||||
|
}
|
||||||
|
QSlider::groove:horizontal:hover {
|
||||||
|
background-color: rgb(55, 62, 76);
|
||||||
|
}
|
||||||
|
QSlider::handle:horizontal {
|
||||||
|
background-color: rgb(189, 147, 249);
|
||||||
|
border: none;
|
||||||
|
height: 10px;
|
||||||
|
width: 10px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
QSlider::handle:horizontal:hover {
|
||||||
|
background-color: rgb(195, 155, 255);
|
||||||
|
}
|
||||||
|
QSlider::handle:horizontal:pressed {
|
||||||
|
background-color: rgb(255, 121, 198);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSlider::groove:vertical {
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 10px;
|
||||||
|
margin: 0px;
|
||||||
|
background-color: rgb(52, 59, 72);
|
||||||
|
}
|
||||||
|
QSlider::groove:vertical:hover {
|
||||||
|
background-color: rgb(55, 62, 76);
|
||||||
|
}
|
||||||
|
QSlider::handle:vertical {
|
||||||
|
background-color: rgb(189, 147, 249);
|
||||||
|
border: none;
|
||||||
|
height: 10px;
|
||||||
|
width: 10px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
QSlider::handle:vertical:hover {
|
||||||
|
background-color: rgb(195, 155, 255);
|
||||||
|
}
|
||||||
|
QSlider::handle:vertical:pressed {
|
||||||
|
background-color: rgb(255, 121, 198);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CommandLinkButton */
|
||||||
|
QCommandLinkButton {
|
||||||
|
color: rgb(255, 121, 198);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
QCommandLinkButton:hover {
|
||||||
|
color: rgb(255, 170, 255);
|
||||||
|
background-color: rgb(44, 49, 60);
|
||||||
|
}
|
||||||
|
QCommandLinkButton:pressed {
|
||||||
|
color: rgb(189, 147, 249);
|
||||||
|
background-color: rgb(52, 58, 71);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Button */
|
||||||
|
#pagesContainer QPushButton {
|
||||||
|
border: 2px solid rgb(52, 59, 72);
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: rgb(52, 59, 72);
|
||||||
|
}
|
||||||
|
#pagesContainer QPushButton:hover {
|
||||||
|
background-color: rgb(57, 65, 80);
|
||||||
|
border: 2px solid rgb(61, 70, 86);
|
||||||
|
}
|
||||||
|
#pagesContainer QPushButton:pressed {
|
||||||
|
background-color: rgb(35, 40, 49);
|
||||||
|
border: 2px solid rgb(43, 50, 61);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user