265 lines
9.9 KiB
Python
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)
|