first implementation to show Comics and MediaFiles

This commit is contained in:
Thomas Peetz
2025-01-05 14:10:15 +01:00
parent 3f65ec55fc
commit 57e7b9e999
10 changed files with 323 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
__pycache__
.idea/
bonus
icons
icons-shadowless
Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 836 B

+89
View File
@@ -0,0 +1,89 @@
from datetime import datetime
import mariadb
from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt
from PySide6.QtGui import QColor
class ComicTableModel(QAbstractTableModel):
def __init__(self, db_config, main_window):
super().__init__()
self.main_window = main_window
self._data = []
self.status_bar = main_window.statusBar
self.mariadb_conn = mariadb.connect(
host=db_config['mariadb']['host'],
port=db_config['mariadb']['port'],
user=db_config['mariadb']['user'],
password=db_config['mariadb']['password'],
database=db_config['mariadb']['database']
)
self.refresh()
def refresh(self):
data = []
cursor = self.mariadb_conn.cursor()
cursor.execute("SELECT id, created_date, last_modified_date, title, publisher_id FROM comic")
rows = cursor.fetchall()
for row in rows:
data.append(list(row))
self.status_bar.showMessage(f"{len(rows)} Einträge geladen", 3000)
self._data = data
def rowCount(self, parent=QModelIndex()):
# The length of the outer list.
return len(self._data)
def headerData(self, col, orientation, role=Qt.ItemDataRole.DisplayRole):
if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole:
match col:
case 0:
return "ID"
case 1:
return "Created"
case 2:
return "Updated"
case 3:
return "Title"
case 4:
return "Verlag"
if orientation == Qt.Orientation.Vertical and role == Qt.ItemDataRole.DisplayRole:
return str(col + 1)
def data(self, index, role=Qt.ItemDataRole.DisplayRole):
value = self._data[index.row()][index.column()]
if role == Qt.ItemDataRole.DisplayRole:
if isinstance(value, datetime):
return value.strftime("%Y-%m-%d %M:%M:%S")
if isinstance(value, str):
return value
if isinstance(value, bytes):
if value == b'\x01':
return "True"
return "False"
return value
if role == Qt.ItemDataRole.DecorationRole:
if isinstance(value, bytes):
# print('{}: {}'.format(value, type(value)))
if value == b'\x01':
return self.main_window.tick
else:
return self.main_window.cross
def columnCount(self, index=QModelIndex()):
# The following takes the first sub-list, and returns
# the length (only works if all rows are an equal length)
return len(self._data[0])
def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
if role == Qt.ItemDataRole.EditRole:
self._data[index.row()][index.column()] = value
if role == Qt.ItemDataRole.CheckStateRole:
checked = value == Qt.CheckState.Checked
self._data[index.row()][index.column()] = checked
return True
def flags(self, index):
return Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsEditable | Qt.ItemFlag.ItemIsUserTristate
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

+131
View File
@@ -0,0 +1,131 @@
"""
PyQT6 GUI for Kontor
"""
import sys
from pathlib import Path
import yaml
from PySide6.QtGui import QAction, QIcon
from PySide6.QtSql import QSqlDatabase
from PySide6.QtWidgets import QWidget, QVBoxLayout, QMenu, QMessageBox, QTabWidget, QTableView
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow
from platformdirs import PlatformDirs
from comic_model import ComicTableModel
from media_file_model import MediaFileTableModel
class MainWindow(QMainWindow):
def __init__(self, config):
super().__init__()
self.tick = QIcon('tick.png')
self.cross = QIcon('cross.png')
self.import_icon = QIcon("application-import.png")
self.export_icon = QIcon("application-export.png")
self.circle_icon = QIcon("arrow-circle-double.png")
self.setWindowTitle("Kontor")
self.setMinimumSize(800, 500)
self._createActions()
self._createMenuBar()
self._createToolBars()
self._createStatusBar()
self.data = []
self.central_widget = QWidget()
parent_layout = QVBoxLayout()
self.central_widget.setLayout(parent_layout)
self.tabs = QTabWidget()
self.tabs.addTab(self.generate_tab_comics(config), "Comics")
self.tabs.addTab(self.generate_tab_media_file(config), "MediaFile")
self.tabs.currentChanged.connect(self._tab_changed)
#label.setAlignment(Qt.AlignmentFlag.AlignCenter)
parent_layout.addWidget(self.tabs)
self.setCentralWidget(self.central_widget)
def _createActions(self):
self.newAction = QAction("&New", self)
self.aboutAction = QAction("&Über...", self)
self.aboutAction.triggered.connect(self.about)
self.importAction = QAction(self.import_icon, "&Import", self)
self.exportAction = QAction(self.export_icon, "&Export", self)
self.refreshAction = QAction(self.circle_icon, "&Refresh", self)
self.refreshAction.triggered.connect(self.refresh)
self.exitAction = QAction("&Beenden", self)
self.exitAction.setShortcut("Alt+F4")
self.exitAction.triggered.connect(self.close)
def _createMenuBar(self):
menu_bar = self.menuBar()
# File menu
file_menu = QMenu("&Datei")
menu_bar.addMenu(file_menu)
file_menu.addAction(self.exitAction)
# Kontor menu
kontor_menu = QMenu("&Kontor")
menu_bar.addMenu(kontor_menu)
kontor_menu.addAction(self.importAction)
kontor_menu.addAction(self.exportAction)
# Help menu
help_menu = QMenu("&Hilfe")
menu_bar.addMenu(help_menu)
help_menu.addAction(self.aboutAction)
def _createToolBars(self):
# Kontor toolbar
kontor_tool_bar = self.addToolBar("Kontor")
kontor_tool_bar.addAction(self.importAction)
kontor_tool_bar.addAction(self.exportAction)
kontor_tool_bar.addAction(self.refreshAction)
def _createStatusBar(self):
self.statusBar = self.statusBar()
self.statusBar.showMessage("Kontor ready", 6000)
self.status_label = QLabel("")
self.statusBar.addPermanentWidget(self.status_label)
def about(self):
QMessageBox.about(self.central_widget, "Über Kontor", f"Python: 3.11\nKontor: 0.1.0")
def refresh(self):
self.data[self.tabs.currentIndex()].refresh()
def _tab_changed(self, tab_index):
self.data[tab_index].refresh()
def generate_tab_comics(self, db_configuration):
comic_tab = QWidget()
layout = QVBoxLayout()
comic_tab.setLayout(layout)
model = ComicTableModel(db_configuration, self)
self.data.append(model)
table_view = QTableView()
table_view.setModel(model)
layout.addWidget(table_view)
return comic_tab
def generate_tab_media_file(self, db_configuration):
media_file_tab = QWidget()
layout = QVBoxLayout()
model = MediaFileTableModel(db_configuration, self)
self.data.append(model)
media_file_tab.setLayout(layout)
table_view = QTableView()
table_view.setModel(model)
layout.addWidget(table_view)
return media_file_tab
if __name__ == '__main__':
app = QApplication(sys.argv)
dirs = PlatformDirs("kontor")
database_config = Path(dirs.user_config_dir, 'database-config.yaml')
with open(database_config, 'rt') as f:
db_config = yaml.safe_load(f.read())
window = MainWindow(db_config)
window.show()
app.exec()
+91
View File
@@ -0,0 +1,91 @@
from datetime import datetime
import mariadb
from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt
from PySide6.QtGui import QColor
class MediaFileTableModel(QAbstractTableModel):
def __init__(self, db_config, main_window):
super().__init__()
self.main_window = main_window
self._data = []
self.status_bar = main_window.statusBar
self.mariadb_conn = mariadb.connect(
host=db_config['mariadb']['host'],
port=db_config['mariadb']['port'],
user=db_config['mariadb']['user'],
password=db_config['mariadb']['password'],
database=db_config['mariadb']['database']
)
self.refresh()
def refresh(self):
data = []
cursor = self.mariadb_conn.cursor()
cursor.execute("SELECT id, url, review, should_download, file_name, cloud_link FROM media_file")
rows = cursor.fetchall()
for row in rows:
data.append(list(row))
self.status_bar.showMessage(f"{len(rows)} Einträge geladen", 3000)
self._data = data
def rowCount(self, parent=QModelIndex()):
# The length of the outer list.
return len(self._data)
def headerData(self, col, orientation, role=Qt.ItemDataRole.DisplayRole):
if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole:
match col:
case 0:
return "ID"
case 1:
return "URL"
case 2:
return "Review"
case 3:
return "Download"
case 4:
return "Filename"
case 5:
return "Cloud Link"
if orientation == Qt.Orientation.Vertical and role == Qt.ItemDataRole.DisplayRole:
return str(col+1)
def data(self, index, role=Qt.ItemDataRole.DisplayRole):
value = self._data[index.row()][index.column()]
if role == Qt.ItemDataRole.DisplayRole:
if isinstance(value, datetime):
return value.strftime("%Y-%m-%d %M:%M:%S")
if isinstance(value, str):
return value
# if isinstance(value, bytes):
# if value == b'\x01':
# return self.main_window.tick
# else:
# return self.main_window.cross
return value
if role == Qt.ItemDataRole.DecorationRole:
if isinstance(value, bytes):
# print('{}: {}'.format(value, type(value)))
if value == b'\x01':
return self.main_window.tick
else:
return self.main_window.cross
def columnCount(self, index=QModelIndex()):
# The following takes the first sub-list, and returns
# the length (only works if all rows are an equal length)
return len(self._data[0])
def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
if role == Qt.ItemDataRole.EditRole:
self._data[index.row()][index.column()] = value
if role == Qt.ItemDataRole.CheckStateRole:
checked = value == Qt.CheckState.Checked
self._data[index.row()][index.column()] = checked
return True
def flags(self, index):
return Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsEditable | Qt.ItemFlag.ItemIsUserTristate
+7
View File
@@ -0,0 +1,7 @@
from PySide6.QtGui import QIcon
tick = QIcon('tick.png')
cross = QIcon('cross.png')
import_icon = QIcon("application-import.png")
export_icon = QIcon("application-export.png")
circle_icon = QIcon("arrow-circle-double.png")
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B