first implementation to show Comics and MediaFiles
This commit is contained in:
@@ -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 |
@@ -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
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 544 B |
+131
@@ -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()
|
||||
@@ -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
|
||||
@@ -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")
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 634 B |
Reference in New Issue
Block a user