master
Glueck Martin 2024-05-28 15:34:07 +02:00
parent fee801a822
commit 0e5286c333
9 changed files with 755 additions and 0 deletions

164
QT/Input_Dialog.py Normal file
View File

@ -0,0 +1,164 @@
import collections
from PySide6 import QtCore, QtWidgets
from QT.Main_Window import Attr_Dict
class Input_Dialog (QtWidgets.QDialog) :
def __init__ (self, title, ** spec) :
super ().__init__ (spec.pop ("parent", None))
self.setWindowTitle (title)
self.layout = L = QtWidgets.QFormLayout (self)
self.widgets = {}
self.min_width = spec.pop ("_MIN_WIDTH", 200)
self.field_spec = spec
self.dependencies = collections.defaultdict (list)
for name, fspec in spec.items () :
type = fspec.get ("type")
default = fspec.get ("default")
if type is None and default is not None :
type = default.__class__.__name__
getattr (self, "_add_%s" % type, self._add_str) \
(L, name, fspec, default, type)
for attr, format in fspec.get ("auto", {}).items () :
self.dependencies [attr].append ((name, format))
layout = QtWidgets.QHBoxLayout ()
ok = QtWidgets.QPushButton ("OK")
ok.clicked.connect (self.store_values)
cancel = QtWidgets.QPushButton ("Cancel")
cancel.clicked.connect (self.reject)
layout.addStretch (10)
layout.addWidget (ok)
layout.addWidget (cancel)
ok.setDefault (True)
L.addRow (layout)
self.setMinimumWidth (self.min_width)
self.result = Attr_Dict ()
# end def __init__
def get_values (self) :
data = Attr_Dict ()
errors = {}
for name, (getter, _) in self.widgets.items () :
data [name] = value = getter ()
fspec = self.field_spec [name]
if not value and fspec.get ("required", False) :
errors [name] = "Required"
return data, errors
# end def store_values
def store_values (self) :
self.result, errors = self.get_values ()
if not errors :
self.accept ()
# end def store_values
def _auto_update (self, changed) :
if changed in self.dependencies :
values, _ = self.get_values ()
for attr, format in self.dependencies [changed] :
if not values.get (attr) :
self.widgets [attr] [1] (format % values)
# end def _auto_update
def _add_str (self, L, name, fspec, default, type) :
widget = QtWidgets.QLineEdit ()
widget.setText (default or "")
self.widgets [name] = widget.text, widget.setText
L.addRow (fspec.get ("title", name.capitalize ()), widget)
widget.editingFinished.connect \
(lambda : self._auto_update (name))
# end def _add_str
def _add_int (self, L, name, fspec, default, type) :
widget = QtWidgets.QSpinBox ()
widget.setRange (-65536, 0x7FFFFFFF)
widget.setValue (default or 0)
self.widgets [name] = widget.value, widget.setValue
L.addRow (fspec.get ("title", name.capitalize ()), widget)
widget.editingFinished.connect \
(lambda : self._auto_update (name))
# end def _add_int
def _add_file_dialog (self, cb, L, name, fspec, default) :
layout = QtWidgets.QHBoxLayout ()
current = QtWidgets.QLineEdit ()
current.setText (default or "")
current.setEnabled (False)
change = QtWidgets.QPushButton ("...")
change.setFixedWidth (24)
change.clicked.connect (lambda : cb (fspec, current))
layout.addWidget (current)
layout.addWidget (change)
layout.setStretch (0, 10)
self.widgets [name] = current.text, current.setText
L.addRow (fspec.get ("title", name.capitalize ()), layout)
self.min_width = max (self.min_width, 600)
# end def _add_file_dialog
def _add_open_filename (self, L, name, fspec, default, type) :
self._add_file_dialog \
(self._choose_file_dialog, L, name, fspec, default)
# end def _add_open_filename
def _choose_file_dialog (self, fspec, current) :
filter = fspec.get ("filter", ())
filter = ";;".join ("%s (%s)" % (t, e) for (t, e) in filter)
file_name = QtWidgets.QFileDialog.getOpenFileName \
( filter = filter
) [0]
if file_name :
current.setText (file_name)
# end def _choose_file_dialog
def _add_save_filename (self, L, name, fspec, default, type) :
self._add_file_dialog \
(self._save_file_dialog, L, name, fspec, default)
# end def _add_open_filename
def _save_file_dialog (self, fspec, current) :
filter = fspec.get ("filter", ())
filter = ";;".join ("%s (%s)" % (t, e) for (t, e) in filter)
file_name = QtWidgets.QFileDialog.getSaveFileName \
( filter = filter
) [0]
if file_name :
current.setText (file_name)
# end def _save_file_dialog
def _add_drop_down (self, L, name, fspec, default, type) :
widget = QtWidgets.QComboBox ()
for idx, e in enumerate \
(sorted (fspec ["choices"], key = lambda e : e ["label"])) :
widget.addItem (e ["label"], e ["value"])
if "tooltip" in e :
widget.setItemData \
(idx, e ["tooltip"], QtCore.Qt.ToolTipRole)
self.widgets [name] = widget.currentText, widget.setCurrentText
L.addRow (fspec.get ("title", name.capitalize ()), widget)
widget.currentTextChanged.connect \
(lambda : self._auto_update (name))
# end def _add_drop_down
def run (self) :
if self.exec () == 1 :
return self.result
# end def run
# end class Input_Dialog
if __name__ == "__main__" :
app = QtWidgets.QApplication ([])
r = Input_Dialog \
( "Test"
, name = {"default": "", "title" : "Layer Name", "required": True}
, file_name = {"default": "", "title" : "Filename"
, "type" : "save_filename"
, "filter": ( ( "Layer Files", "*.layer")
, ( "Json Files", "*.json")
, ( "All Files", "*.*")
)
, "auto" : {"name" : "%(name)s/%(name)s.layer"}
}
).run ()
if r is not None :
print (r)

114
QT/Main_Window.py Normal file
View File

@ -0,0 +1,114 @@
from PySide6 import QtWidgets, QtCore, QtGui
import os
class Attr_Dict (dict) :
def __getattr__ (self, key) :
return self [key]
# end def __getattr__
# end class Attr_Dict
class Main_Window (QtWidgets.QMainWindow) :
DEFAULT_SETTINGS_FILE = "settings.ini"
APP_NAME = "Main Window"
VERSION = "0"
APP_ORGANISATION = ("Org-Name", "Org-Domain")
def __init__ (self, app, settings) :
super ().__init__ ()
self.settings = settings
self.setWindowTitle ("%s %s" % (self.APP_NAME, self.VERSION))
self._setupActions (app)
self._setupGui ()
if not self.restore_settings (settings) :
self.show ()
# end def __init__
def _setupActions (self, app) :
self.actions = Attr_Dict ()
self._add_action ("exit", "Exit", self.close, icon = ":/icons/exit.png")
# end def _setupActions
def _add_action (self, name, label, callback, icon = None, ** options) :
A = QtGui.QAction (label, self, ** options)
if icon :
A.setIcon (QtGui.QIcon (icon))
self.actions [name] = A
A.triggered.connect (callback)
# end def _add_action
def _setupGui (self) :
pass
# end def _setupGui
def closeEvent (self, * args) :
self.save_settings ()
super ().closeEvent (* args)
# end def closeEvent
@classmethod
def _open_settings (cls, settings_file = None) :
if not settings_file :
settings_file = os.path.join (cls.APP_DIR, cls.DEFAULT_SETTINGS_FILE)
QtCore.QCoreApplication.setOrganizationName (cls.APP_ORGANISATION [0])
QtCore.QCoreApplication.setOrganizationDomain (cls.APP_ORGANISATION [1])
QtCore.QCoreApplication.setApplicationName (cls.APP_NAME)
return QtCore.QSettings (settings_file, QtCore.QSettings.IniFormat)
# end def _open_settings
@classmethod
def load_settings (cls, cmd) :
settings = cls._open_settings (cmd.settings_file)
ps = cls._load_app_settings (settings)
settings.beginGroup ("TempSettings")
settings.setValue ("settingsFile", cmd.settings_file)
for n, v in ps.items () :
if getattr (cmd, n, None) != None :
v = getattr (cmd, n)
settings.setValue (n, v)
settings.endGroup ()
return settings
# end def load_settings
def restore_settings (self, settings) :
self.settings = settings
QtCore.QTimer.singleShot (1, self._restore_settings)
# end def restore_settings
def _restore_settings (self) :
settings = self.settings
settings.beginGroup ("MainWindow")
self.restoreGeometry (settings.value ("geometry" ))
self.restoreState (settings.value ("windowState"))
settings.endGroup ()
# end def _restore_settings
def save_settings (self) :
settings = self.settings
settings.beginGroup ("MainWindow")
settings.setValue ("geometry", self.saveGeometry ())
settings.setValue ("windowState", self.saveState ())
settings.endGroup ()
# end def save_settings
@classmethod
def run (cls, settings) :
QtWidgets.QApplication.setStyle ("macintosh")
app = QtWidgets.QApplication ([])
if cls.pre_main_window_action (settings) :
self = cls (app, settings)
if self.isVisible () :
app.exec_ ()
# end def run
@classmethod
def pre_main_window_action (cls, settings) :
return True
# end def pre_main_window_action
# end class Main_Window
### __END__ Main_Window

448
QT/Model_Adapter.py Normal file
View File

@ -0,0 +1,448 @@
# Simplified tree model (based on a QtAbstractItemModel)
from PySide2 import QtCore, QtWidgets
from Accessor import Getter
import contextlib
Qt = QtCore.Qt
class Column :
"""Column definition"""
Flag_Map = dict \
( no_flag = (Qt.NoItemFlags, False)
, selectable = (Qt.ItemIsSelectable, True)
, enabled = (Qt.ItemIsEnabled, True)
, editable = (Qt.ItemIsEditable, False)
, drag = (Qt.ItemIsDragEnabled, False)
, drop = (Qt.ItemIsDropEnabled, False)
, checkable = (Qt.ItemIsUserCheckable, False)
, tristate = (Qt.ItemIsTristate, False)
)
Clone_Attributes = ("name", "attr", "getter", "_flags", "model")
def __init__ ( self, name, attr = None, getter = None, model = None
, ** flags
) :
self.name = name
self.attr = attr or name.lower ()
self.getter = getter or getattr (Getter, self.attr)
self.model = model
self._flags = flags
# end def __init__
def background (self, row) :
return None
# end def background
def __call__ (self, row) :
return self.getter (row)
# end def __call__
def clone (self, ** overrides) :
d = dict \
( (a, overrides.get (a, getattr (self, a)))
for a in self.Clone_Attributes
)
d.update (d.pop ("_flags", {}))
return self.__class__ (** d)
# end def clone
def checkstate (self, row) :
return None
# end def checkstate
def decoration (self, row) :
return None
# end def decoration
def flags (self, row) :
result = 0
for attr, (flag, default) in self.Flag_Map.items () :
v = self._flags.get (attr, default)
if callable (v) :
v = v (row)
if v :
result |= flag
return result
# end def flags
def font (self, row) :
return None
# end def font
def foreground (self, row) :
return None
# end def foreground
def text_aligment (self, row) :
return None
# end def text_aligment
# end class Column
class _Root_Item_ :
"""Simple models which can be used as hidden root element of no real root
element exists in the model
"""
parent = None
def __init__ (self, * children) :
self.children = []
for c in children :
self.append (c)
# end def __init__
def append (self, child) :
self.children.append (child)
child.parent = self
# end def append
def index (self, child) :
return self.children.index (child)
# end def index
def __delitem__ (self, key) :
del self.children [key]
# end def __delitem__
def __getitem__ (self, key) :
return self.children [key]
# end def __getitem__
def __len__ (self) :
return len (self.children)
# end def __len__
def __setitem__ (self, key, value) :
self.children [key] = value
if not isinstance (value, (list, tuple)) :
value = (value, )
for v in value :
v.parent = self
# end def __setitem__
def __str__ (self) :
return "<Root-Item>"
# end def __str__
# end class _Root_Item_
class Model_Adapter (QtCore.QAbstractItemModel) :
"""The underlying QT class handling the interface to the wrapper class
"""
### QT Interface
def columnCount (self, parent) :
return len (self.Columns)
# end def columnCount
def data (self, index, role) :
#print ("data", role)
if not index.isValid () :
return None
column = index.column ()
item = index.internalPointer ()
col_desc = self.Columns [column]
if role == Qt.DisplayRole or role == Qt.EditRole :
return col_desc (item)
elif role == Qt.CheckStateRole :
return col_desc.checkstate (item)
elif role == Qt.BackgroundRole :
return col_desc.background (item)
elif role == Qt.ForegroundRole :
return col_desc.foreground (item)
elif role == Qt.FontRole :
return col_desc.font (item)
elif role == Qt.DecorationRole :
return col_desc.decoration (item)
elif role == Qt.TextAlignmentRole :
return col_desc.text_aligment (item)
return None
# end def data
def flags (self, index) :
if index.isValid () :
data = index.internalPointer ()
col = index.column ()
return self.Columns [col].flags (data)
return super ().flags (index)
# end def flags
def index (self, row, column, parent_index, parent = None) :
#debug = "ID: %d <%-7s>" % \
# (row, self.item_path (parent_index, ", ") or "R")
if parent is None :
if not parent_index.isValid () :
parent = self.root_item
else :
parent = parent_index.internalPointer ()
try :
child = parent [row]
#print ("%s, P<%s>, C<%s>" %(1, parent, child))
return self.createIndex (row, column, child)
except IndexError as exc:
#print (debug, exc)
return self.invalid_index
# end def index
def index_for_item (self, item, column = 0) :
if item.parent :
row = item.parent.index (item)
return self.createIndex (row, column, item)
return self.invalid_index
# end def index_for_item
def item_from_index (self, index) :
if index.isValid () :
return index.internalPointer ()
# end def item_from_index
@property
def invalid_index (self) :
return QtCore.QModelIndex ()
# end def invalid_index
def parent (self, index) :
if not index.isValid () :
return self.invalid_index
item = index.internalPointer ()
parent = item.parent
if parent is self.root_item :
return self.invalid_index
if parent.parent :
row = parent.parent.index (parent)
else :
row = 0
return self.createIndex (row, 0, parent)
# end def parent
def rowCount (self, parent_index) :
if not parent_index.isValid () :
parent = self.root_item
else:
parent = parent_index.internalPointer ()
#path = self.item_path (parent_index, ", ") or "R"
#print ("RC: %-7s [%d]" % (path, len (parent)))
return len (parent)
# end def rowCount
def setData (self, index, value, role) :
if index.isValid () :
if self.set_data (index, value, role) :
self.dataChanged.emit (index, index)
return True
return False
# end def setData
### QT Optional
def headerData (self, section, orientation, role) :
if ( (orientation == Qt.Horizontal)
and (role == Qt.DisplayRole)
) :
return self.Columns [section].name
return None
# end def headerData
def hasChildren (self, index) :
result = True
if index.isValid () :
result = bool (len (index.internalPointer ()))
#path = self.item_path (parent_index, ", ") or "R"
#print ("HC: %-7s [%s]" % (path, result))
return result
# end def hasChildren
### mime data handling
def supportedDropActions (self) :
default = super ().supportedDropActions ()
return getattr (self, "supported_drop_actions", default)
# end def supportedDropActions
def mimeTypes (self) :
model_fct = getattr (self, "mime_types", None)
result = super ().mimeTypes ()
if model_fct is not None :
result = model_fct (result)
return result
# end def mimeTypes
def mimeData (self, indexes) :
model_fct = getattr (self, "mime_data", None)
if model_fct :
items = [i.internalPointer () for i in indexes]
result = QtCore.QMimeData ()
model_fct (result, items, indexes)
return result
return super ().mimeData (indexes)
# end def mimeData
def dropMimeData (self, mime_data, action, row, column, parent) :
model_fct = getattr (self, "drop_mime_data", None)
if model_fct is None :
return super ().dropMimeData \
(mime_data, action, row, column, parent)
return model_fct \
(mime_data, action, row, column, parent)
# end def dropMimeData
### Python Interface
Columns = ()
Supported_Drop_Actions = ()
mimeTypes = ()
def __init__ (self, * items, ** kw) :
super ().__init__ ()
self.Columns = [c.clone (model = self) for c in self.Columns]
root_item = kw.pop ("root", None)
if root_item is None :
root_item = _Root_Item_ (* items)
self.root_item = root_item
# end def __init__
def append (self, item, parent = None) :
if parent is None :
parent = self.root_item
start = len (parent)
index = self.index_for_item (parent)
self.beginInsertRows (index, start, start)
parent.append (item)
self.endInsertRows ()
# end def append
def dump (self, root_index = None, indend = "") :
if root_index is None :
root_index = self.invalid_index
result = []
for i in range (self.rowCount (root_index)) :
index = self.index (i, 0, root_index)
item = self.item_from_index (index)
result.append ("%s%s" % (indend, item))
result.extend (self.dump (index, indend + "."))
if not indend :
print ("---- Dump by index functions")
print ("\n".join (result))
print ("---- Dump end")
return result
# end def dump
def _walk (self, path, root, root_index = None) :
for n in range (len (root)) :
c = root [n]
p = path + (n, )
if root_index is not None :
index = self.index (n, 0, root_index)
yield p, c, index
yield from self._walk (p, c, index)
else :
yield p, c
yield from self._walk (p, c)
# end def _walk
def walk (self, with_index = False) :
index = None
if with_index :
index = self.invalid_index
return self._walk ((), self.root_item, index)
# end def walk
def item_path (self, index, joiner = None) :
result = []
if index.isValid () :
result.extend (self.item_path (self.parent (index)))
result.append (index.row ())
if joiner :
result = joiner.join (result)
return result
# end def item_path
def qindex (self, item, col = 0) :
if item is self.root_item :
row = 0
else :
row = item.parent.index (item)
return self.createIndex (row, col, item.parent)
# end def qindex
def insert_row (self, item, parent_index = None, start = -1) :
count = 1
parent_index = parent_index or self.invalid_index
if start == -1 :
start = self.rowCount (parent_index)
if parent_index.isValid () :
parent = parent_index.internalPointer ()
else :
parent = self.root_item
self.beginInsertRows (parent_index, start, start + count - 1)
parent [start:start] = [item]
self.endInsertRows ()
return self.index (start, 0, parent_index)
# end def insert_row
def item_from_index (self, index) :
if index.isValid () :
return index.internalPointer ()
# end def item_from_index
def remove (self, index = None, item = None) :
if index is None :
parent = item.parent
row = parent.index (item)
parent_index = self.qindex (parent)
else :
row = index.row ()
parent_index = index.parent ()
if parent_index.isValid () :
parent = parent_index.internalPointer ()
else :
parent = self.root_item
item = parent [row]
self.beginRemoveRows (parent_index, row, row + 1)
del parent [row]
if hasattr (item, "delete") :
item.delete ()
self.endRemoveRows ()
# end def remove
@contextlib.contextmanager
def replace_model (self) :
self.beginResetModel ()
yield
self.endResetModel ()
# end def replace_model
def set_data (self, index, value, role) :
return False
# end def set_data
# end class Model_Adapter
class Tree_View (QtWidgets.QTreeView) :
def expanded_nodes (self) :
expanded_path = []
model = self.model ()
if model :
for p, _, index in model.walk (True) :
if self.isExpanded (index) :
expanded_path.append (p)
return expanded_path
# end def expanded_nodes
def restore_expanded_nodes (self, expanded) :
model = self.model ()
if model :
indixes = {}
iv_index = model.invalid_index
for path in expanded :
parent_index = indixes.get (path [:-1], iv_index)
index = model.index (path [-1], 0, parent_index)
indixes [path] = index
item = model.item_from_index (index)
if item and len (item) :
self.setExpanded (index, True)
# end def restore_expanded_nodes
# end class Tree_View
### __END__ QT.Model_Adapter

24
QT/Push_Button.py Normal file
View File

@ -0,0 +1,24 @@
from PySide2 import QtWidgets, QtGui
def Push_Button (text, clicked = None, icon = None) :
button = QtWidgets.QPushButton (text)
if clicked :
button.clicked.connect (clicked)
if icon :
button.setIcon (QtGui.QIcon (icon))
return button
# end def Push_Button
#class Push_Button (QtWidgets.QPushButton) :
#
# def __init__ ( self, text
# , callback = None
# , icon = None
# ) :
# super ().__init__ (text)
# if callback :
# self.clicked.connect (callback)
# if icon :
# self.setIcon (QtGui.QIcon (icon))
# # end def

2
QT/Screen_Designer.ini Normal file
View File

@ -0,0 +1,2 @@
[TempSettings]
settingsFile=@Invalid()

3
QT/__init__.py Normal file
View File

@ -0,0 +1,3 @@
### package for commone QT classes
from PySide6 import QtWidgets, QtCore, QtGui, QtCore
QT = QtCore.Qt

Binary file not shown.

Binary file not shown.

Binary file not shown.