GarageCalc1/src/clsTableWidget.py

265 lines
9.9 KiB
Python

#!/usr/bin/env python3
"""
project: clsTableWidget
file: clsTableWidget.py
summary: Implements my own TableWidget class
author: Paul Salajean (p.salajean[at]gmx.de)
license: GPL
version: 0.1
"""
# Standard library imports
import sys
import os
# Third party imports
from PySide2.QtWidgets import QApplication, QTableWidget, QAbstractItemView, QTableWidgetItem, QMenu, \
QMainWindow, QMessageBox
from PySide2.QtCore import Qt, QItemSelectionModel, QCoreApplication
from PySide2.QtGui import QIcon
# local globals
SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__))
ICON_CLEAR = SCRIPT_PATH + "./img/icons8-clear-symbol-16.png"
ICON_COPY = SCRIPT_PATH + "./img/icons8-copy-16.png"
ICON_ERASER = SCRIPT_PATH + "/img/icons8-eraser-16.png"
ICON_INSERT = SCRIPT_PATH + "/img/icons8-insert-clip-16.png"
ICON_APPEND = SCRIPT_PATH + "/img/icons8-append-clip-16.png"
ICON_PASTE = SCRIPT_PATH + "/img/icons8-paste-16.png"
ICON_CUT = SCRIPT_PATH + "/img/icons8-scissors-16.png"
ICON_ADD_ROW = SCRIPT_PATH + "/img/icons8-add-row-16.png"
ICON_DEL = SCRIPT_PATH + "/img/icons8-delete-16.png"
# def resource_path(relative_path):
# """ Get absolute path to resource, works for dev and for PyInstaller """
# try:
# # PyInstaller creates a temp folder and stores path in _MEIPASS
# base_path = sys._MEIPASS
# print("base_path", base_path)
# except Exception:
# base_path = os.path.abspath(".")
# return os.path.join(base_path, relative_path)
class TableWidget(QTableWidget):
def __init__(self, parent=None):
super().__init__()
self.parent = parent
self.setParent(parent)
# self.setSelectionMode(QAbstractItemView.ContiguousSelection)
self.setSelectionMode(QAbstractItemView.SingleSelection)
self.setSelectionBehavior(QTableWidget.SelectItems)
#self.setSelectionBehavior(QTableWidget.SelectRows)
self.setAlternatingRowColors(True)
self.setSortingEnabled(True)
self.horizontalHeader().setSectionsMovable(True)
self.horizontalHeader().setStretchLastSection(True)
# Context-menu
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.on_context_menu)
# self.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed | QAbstractItemView.AnyKeyPressed)
self.vertHeader = self.verticalHeader()
self.vertHeader.setContextMenuPolicy(Qt.CustomContextMenu)
self.vertHeader.customContextMenuRequested.connect(self.on_rowheadercontext_menu)
self.vertHeader.setSelectionMode(QAbstractItemView.SingleSelection)
self.vertHeader.sectionClicked.connect(self.select_row)
self.vertHeader.setSectionsMovable(True)
# optimize row height
self.resizeRowsToContents()
self.row_selected = False
def select_row(self, row):
self.setSelectionBehavior(QTableWidget.SelectRows)
self.selectRow(row)
self.setSelectionBehavior(QTableWidget.SelectItems)
self.row_selected = True
def on_context_menu(self, position):
menu = QMenu()
item_cut = menu.addAction(QIcon(ICON_CUT), QCoreApplication.translate("TableWidget", "Cut") + "\tCtrl+X")
item_copy = menu.addAction(QIcon(ICON_COPY), QCoreApplication.translate("TableWidget", "Copy") + "\tCtrl+C")
item_paste = menu.addAction(QIcon(ICON_PASTE), QCoreApplication.translate("TableWidget", "Paste") + "\tCtrl+V")
menu.addSeparator()
print(ICON_ERASER)
item_delete = menu.addAction(QIcon(ICON_ERASER), QCoreApplication.translate("TableWidget", "Delete") + "\tDel")
ac = menu.exec_(self.mapToGlobal(position))
if ac == item_cut:
self.item_cut()
elif ac == item_copy:
self.item_copy()
elif ac == item_paste:
self.item_paste()
elif ac == item_delete:
self.item_del()
def on_rowheadercontext_menu(self, position):
menu = QMenu()
row_cut = menu.addAction(QIcon(ICON_CUT), QCoreApplication.translate("TableWidget", "Cut row"))
row_copy = menu.addAction(QIcon(ICON_COPY), QCoreApplication.translate("TableWidget", "Copy row"))
row_paste = menu.addAction(QIcon(ICON_PASTE), QCoreApplication.translate("TableWidget", "Paste row"))
menu.addSeparator()
row_insert_before = menu.addAction(QIcon(ICON_INSERT), QCoreApplication.translate("TableWidget", "Insert row before"))
row_insert_after = menu.addAction(QIcon(ICON_APPEND), QCoreApplication.translate("TableWidget", "Insert row after"))
menu.addSeparator()
row_remove = menu.addAction(QIcon(ICON_DEL), QCoreApplication.translate("TableWidget", "Remove row"))
row_delete_items = menu.addAction(QIcon(ICON_ERASER), QCoreApplication.translate("TableWidget", "Delete items"))
ac = menu.exec_(self.mapToGlobal(position))
row = self.vertHeader.logicalIndexAt(position)
if ac == row_cut:
self.item_cut()
elif ac == row_copy:
self.item_copy()
elif ac == row_paste:
self.item_paste()
elif ac == row_insert_before:
self.row_insert(row)
elif ac == row_insert_after:
self.row_insert(row+1)
elif ac == row_remove:
self.row_remove(row)
elif ac == row_delete_items:
self.row_delete_items()
def row_insert(self, row):
self.insertRow(row)
def has_data(self, row):
""" Check if target already contains data. """
# sel_idx = self.selectionModel().selectedIndexes()
for col in range(self.columnCount()):
item = self.item(row, col)
if item:
if len(item.text()) > 0:
return True
return False
def row_remove(self, row):
if self.has_data(row):
msg = QCoreApplication.translate("TableWidget", "Row Nr.") + " " + str(row+1) + " " + QCoreApplication.translate("TableWidget", "to be removed?")
reply = QMessageBox.question(self, QCoreApplication.translate("TableWidget", "Remove"), msg, \
QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
if reply == QMessageBox.No:
return False
self.removeRow(row)
return True
def row_delete_items(self):
self.item_del()
def keyPressEvent(self, event):
super().keyPressEvent(event)
modifiers = QApplication.keyboardModifiers()
key = event.key()
if modifiers == Qt.ControlModifier:
if key == Qt.Key_C:
self.item_copy()
elif key == Qt.Key_V:
self.item_paste()
elif key == Qt.Key_X:
self.item_cut()
elif key == Qt.Key_A:
self.setSelectionMode(QAbstractItemView.ContiguousSelection)
self.selectAll()
self.setSelectionMode(QAbstractItemView.SingleSelection)
if key == Qt.Key_Delete:
self.item_del()
elif key == Qt.Key_Escape:
self.clearSelection()
def item_paste(self):
cur_row = self.currentRow()
cur_col = self.currentColumn()
# ask_confirmation = True
if self.row_selected:
cur_col = 0
col = 0
if len(self.clipboard_data) == 1:
data = self.clipboard_data[0]
item = QTableWidgetItem(data)
self.setItem(cur_row, cur_col, item)
item.setSelected(True)
else:
for data in self.clipboard_data:
item = QTableWidgetItem(data)
# if item:
# if len(item.text()) >0:
# if ask_confirmation:
# msg = QCoreApplication.translate("TableWidget", "Zelle enthält bereits Daten. Überschreiben?")
# reply = QMessageBox.question(self, QCoreApplication.translate("TableWidget", "Überschreiben"), msg, \
# QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
# if reply == QMessageBox.No:
# return False
# ask_confirmation = False
self.setItem(cur_row, col, item)
item.setSelected(True)
col += 1
def item_cut(self):
self.item_copy()
sel_idx = self.selectedIndexes()
for idx in sel_idx:
item = self.itemFromIndex(idx)
try:
item.setData(Qt.DisplayRole, None)
except AttributeError:
pass
def item_copy(self):
sel_idx = self.selectedIndexes()
sel_rows = self.selectionModel().selectedRows()
if len(sel_idx) == 1:
self.row_selected = False
self.sel_ranges = self.selectedRanges()
self.clipboard_data = []
for idx in sel_idx:
self.clipboard_data.append(idx.data())
def item_del(self):
ask_cofirmation = True
sel_idx = self.selectionModel().selectedIndexes()
for idx in sel_idx:
row = idx.row()
col = idx.column()
item = self.item(row, col)
if item:
if self.has_data(row) and ask_cofirmation:
msg = QCoreApplication.translate("TableWidget", "Delete cell content?")
reply = QMessageBox.question(self, QCoreApplication.translate("TableWidget", "Delete"), msg, \
QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
if reply == QMessageBox.No:
return False
ask_cofirmation = False
item.setData(Qt.DisplayRole, None)