Commit 03b2e0b3 authored by Benjamin Jakimow's avatar Benjamin Jakimow
Browse files

updated qps

parent ddbc7e80
......@@ -27,7 +27,7 @@
import sys, importlib, site, os, pathlib, typing
from qgis.core import QgsApplication
from qgis.gui import QgisInterface, QgsMapLayerConfigWidgetFactory
__version__ = '0.3'
__version__ = '1.0'
DIR_UI_FILES = pathlib.Path(__file__).parent / 'ui'
DIR_ICONS = DIR_UI_FILES / 'icons'
......
......@@ -77,7 +77,6 @@ def hasClassification(pathOrDataset):
:return: True | False
"""
ds = None
from qps.utils import gdalDataset
try:
if isinstance(pathOrDataset, gdal.Dataset):
ds = pathOrDataset
......@@ -129,7 +128,7 @@ class ClassInfo(QObject):
self.mName = name
self.mLabel = label
self.mColor = color
self.mColor = None
if color:
self.setColor(color)
......@@ -169,6 +168,8 @@ class ClassInfo(QObject):
Sets the class color.
:param color: QColor
"""
if not isinstance(color, QColor):
color = QColor(color)
assert isinstance(color, QColor)
self.mColor = color
self.sigSettingsChanged.emit()
......@@ -208,7 +209,9 @@ class ClassInfo(QObject):
Create a copy of this ClassInfo
:return: ClassInfo
"""
return ClassInfo(name=self.mName, color=self.mColor)
return ClassInfo(name=self.mName,
color=self.mColor,
label=self.mLabel)
def __hash__(self):
return hash(id(self))
......@@ -250,12 +253,14 @@ class ClassificationScheme(QAbstractTableModel):
sigIsEditableChanged = pyqtSignal(bool)
def __init__(self, name: str = None):
def __init__(self, name: str = None, zero_based: bool = False):
super(ClassificationScheme, self).__init__()
self.mClasses = []
self.mName = name
self.mIsEditable = True
self.mZeroBased: bool = zero_based
if name is None:
name = 'Classification'
......@@ -427,6 +432,8 @@ class ClassificationScheme(QAbstractTableModel):
return 'Class color "{}"'.format(classInfo.color().name())
if role == Qt.EditRole:
if col == 0:
return classInfo.label()
if col == 1:
return classInfo.name()
if col == 2:
......@@ -452,6 +459,9 @@ class ClassificationScheme(QAbstractTableModel):
classInfo = self.index2ClassInfo(row)
b = False
if role == Qt.EditRole:
if col == 0:
classInfo.setLabel(int(value))
b = True
if col == 1:
classInfo.setName(value)
b = True
......@@ -470,8 +480,11 @@ class ClassificationScheme(QAbstractTableModel):
flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
if self.mIsEditable:
flags |= Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled
if col == 1 and self.isEditable():
flags |= Qt.ItemIsEditable
if self.isEditable():
if col == 0 and not self.mZeroBased:
flags |= Qt.ItemIsEditable
elif col == 1:
flags |= Qt.ItemIsEditable
return flags
def headerData(self, section: int, orientation: Qt.Orientation, role: int = Qt.DisplayRole):
......@@ -544,7 +557,10 @@ class ClassificationScheme(QAbstractTableModel):
with open(p, 'r') as f:
jsonStr = f.read()
return ClassificationScheme.fromJson(jsonStr)
elif p.endswith('.csv'):
return ClassificationScheme.fromCsv(p)
elif p.endswith('.qml'):
return ClassificationScheme.fromQml(p)
except Exception as ex:
print(ex, file=sys.stderr)
return None
......@@ -574,9 +590,9 @@ class ClassificationScheme(QAbstractTableModel):
classes = []
for classInfo in self:
qgsClass = QgsPalettedRasterRenderer.Class(
classInfo.label(),
classInfo.color(),
classInfo.name())
int(classInfo.label()),
color=classInfo.color(),
label=classInfo.name())
classes.append(qgsClass)
renderer = QgsPalettedRasterRenderer(None, band, classes)
return renderer
......@@ -761,7 +777,11 @@ class ClassificationScheme(QAbstractTableModel):
def _updateLabels(self):
"""
Assigns class labels according to the ClassInfo position
in case the ClassificationScheme is zero-based
"""
if not self.mZeroBased:
return
for i, c in enumerate(self.mClasses):
c.mLabel = i
self.dataChanged.emit(self.createIndex(0, 0),
......@@ -819,7 +839,7 @@ class ClassificationScheme(QAbstractTableModel):
color = QColor(nextCol)
nextCol = nextColor(nextCol)
name = 'Class {}'.format(j)
classes.append(ClassInfo(name=name, color=color))
classes.append(ClassInfo(name=name, color=color, label=j))
self.insertClasses(classes)
def addClasses(self, classes, index=None):
......@@ -862,10 +882,6 @@ class ClassificationScheme(QAbstractTableModel):
self._updateLabels()
self.sigClassesAdded.emit(classes)
# sigClassInfoChanged = pyqtSignal(ClassInfo)
# def onClassInfoSettingChanged(self, *args):
# self.sigClassInfoChanged.emit(self.sender())
def classIndexFromValue(self, value, matchSimilarity=False) -> int:
"""
Get a values and returns the index of ClassInfo that matches best to.
......@@ -1110,19 +1126,25 @@ class ClassificationScheme(QAbstractTableModel):
:return: ClassificationScheme, None if classes are undefined.
"""
assert isinstance(band, gdal.Band)
cat = band.GetCategoryNames()
ct = band.GetColorTable()
if cat is None or len(cat) == 0:
cat = band.GetCategoryNames()
if not isinstance(cat, list) or len(cat) == 0:
return None
scheme = ClassificationScheme()
classes = []
for i, catName in enumerate(cat):
cli = ClassInfo(name=catName, label=i)
classInfo: ClassInfo = ClassInfo(name=catName, label=i)
NULL = QVariant()
if ct is not None:
cli.setColor(QColor(*ct.GetColorEntry(i)))
classes.append(cli)
gdal_color = ct.GetColorEntry(i)
if gdal_color in [None, NULL]:
gdal_color = (0, 0, 0, 0)
classInfo.setColor(QColor(*gdal_color))
classes.append(classInfo)
if len(classes) == 0:
return None
scheme.insertClasses(classes)
return scheme
......@@ -1202,6 +1224,7 @@ class ClassificationScheme(QAbstractTableModel):
# read CSV data
reader = csv.DictReader(lines[i:], delimiter=delimiter)
iLabel = None
iName = None
iColor = None
for i, name in enumerate(reader.fieldnames):
......@@ -1209,6 +1232,8 @@ class ClassificationScheme(QAbstractTableModel):
iName = i
if iColor is None and re.search(r'color', name, re.I):
iColor = i
if iLabel is None and re.search(r'label', name, re.I):
iLabel = i
rows = [row for row in reader]
nc = len(rows)
......@@ -1219,6 +1244,8 @@ class ClassificationScheme(QAbstractTableModel):
for i, row in enumerate(rows):
c = cs[i]
assert isinstance(c, ClassInfo)
if iLabel is not None:
c.setLabel(int(row[fieldnames[iLabel]]))
if iName is not None:
c.setName(row[fieldnames[iName]])
if iColor is not None:
......@@ -1610,7 +1637,7 @@ class ClassificationSchemeWidget(QWidget):
"Read classes from text file",
"/home", filter)
if isinstance(path, str) and os.path.isfile(path):
cs = ClassificationScheme.fromFile()
cs = ClassificationScheme.fromFile(path)
if isinstance(cs, ClassificationScheme):
self.mScheme.insertClasses(cs[:])
......@@ -1862,12 +1889,78 @@ class ClassificationSchemeEditorConfigWidget(QgsEditorConfigWidget):
def __init__(self, vl: QgsVectorLayer, fieldIdx: int, parent: QWidget):
super(ClassificationSchemeEditorConfigWidget, self).__init__(vl, fieldIdx, parent)
# self.setupUi(self)
assert isinstance(vl, QgsVectorLayer)
self.mSchemeWidget = ClassificationSchemeWidget(parent=self)
self.mSchemeWidget.sigValuesChanged.connect(self.changed)
self.mSchemeWidget.classificationScheme().sigClassesAdded.connect(self.onClassesAdded)
self.mSchemeWidget.classificationScheme().sigClassesRemoved.connect(self.onClassesRemoved)
self.mActionImport = QAction('Import from layer')
self.mActionImport.setIcon(QIcon(':/qps/ui/icons/import_symbology.svg'))
self.mActionImport.triggered.connect(self.onImportFromLayer)
self.mActionExport = QAction('Set to layer')
self.mActionExport.triggered.connect(self.onExportToLayer)
self.mActionExport.setIcon(QIcon(':/qps/ui/icons/export_symbology.svg'))
self.btnImport = QToolButton(parent=self.mSchemeWidget)
self.btnExport = QToolButton(parent=self.mSchemeWidget)
self.btnImport.setDefaultAction(self.mActionImport)
self.btnExport.setDefaultAction(self.mActionExport)
self.btnImport.setAutoRaise(True)
self.btnExport.setAutoRaise(True)
l: QVBoxLayout = self.mSchemeWidget.btnBarLayout
l.addWidget(self.btnImport)
l.addWidget(self.btnExport)
self.setLayout(QVBoxLayout())
self.layout().addWidget(self.mSchemeWidget)
vl.rendererChanged.connect(self.updateButtons)
self.mLastConfig = {}
def onImportFromLayer(self):
lyr: QgsVectorLayer = self.layer()
if isinstance(lyr, QgsVectorLayer):
r = lyr.renderer()
if isinstance(r, QgsCategorizedSymbolRenderer):
cs = ClassificationScheme.fromFeatureRenderer(r)
if len(cs) > 0:
self.classificationScheme().clear()
self.classificationScheme().insertClasses(cs[:])
def onExportToLayer(self):
cs = self.classificationScheme()
if isinstance(cs, ClassificationScheme) and len(cs) > 0:
renderer = cs.featureRenderer()
renderer.setClassAttribute(self.layer().fields().at(self.field()).name())
self.layer().setRenderer(renderer)
def onClassesAdded(self):
self.updateButtons()
def onClassesRemoved(self):
self.updateButtons()
def classificationScheme(self) -> ClassificationScheme:
return self.mSchemeWidget.classificationScheme()
def updateButtons(self):
self.mActionExport.setEnabled(len(self.classificationScheme()) > 0)
lyr: QgsVectorLayer = self.layer()
has_classes = False
if isinstance(lyr, QgsVectorLayer):
r = lyr.renderer()
if isinstance(r, QgsCategorizedSymbolRenderer):
has_classes = r.classAttribute() in self.layer().fields().names()
self.mActionImport.setEnabled(has_classes)
def config(self, *args, **kwargs) -> dict:
return classSchemeToConfig(self.mSchemeWidget.classificationScheme())
......
......@@ -30,7 +30,7 @@
<number>2</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="btnBarLayout">
<property name="spacing">
<number>2</number>
</property>
......@@ -224,7 +224,10 @@
</action>
<action name="actionLoadFromRasterFile">
<property name="text">
<string>Load from raster file</string>
<string>Load from Raster Image</string>
</property>
<property name="toolTip">
<string>Load from Raster Image</string>
</property>
</action>
<action name="actionLoadFromMapLayer">
......
# PyQtGraph Library:
PyQtGraph is a poweful library for interactive scientific plotting based on Qt
PyQtGraph is a powerful library for interactive scientific plotting based on Qt
PyQtGrapg was embedded into QPS as described in https://pyqtgraph.readthedocs.io/en/latest/how_to_use.html#embedding-pyqtgraph-as-a-sub-package-of-a-larger-project
## Links
......@@ -13,7 +13,7 @@ https://pyqtgraph.readthedocs.io/en/latest/
Please note that PyQtGraph is distributed under the following licence:
Copyright (c) 2012 University of North Carolina at Chapel Hill
Luke Campagnola ('luke.campagnola@%s.com' % 'gmail')
Luke Campagnola ('luke.campagnola@%s.com' % 'gmail')
The MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
......
# -*- coding: utf-8 -*-
import weakref
import warnings
from ..Qt import QtCore, QtGui
from ..Point import Point
from .. import functions as fn
......@@ -88,15 +90,11 @@ class GraphicsScene(QtGui.QGraphicsScene):
@classmethod
def registerObject(cls, obj):
"""
Workaround for PyQt bug in qgraphicsscene.items()
All subclasses of QGraphicsObject must register themselves with this function.
(otherwise, mouse interaction with those objects will likely fail)
"""
if HAVE_SIP and isinstance(obj, sip.wrapper):
cls._addressCache[sip.unwrapinstance(sip.cast(obj, QtGui.QGraphicsItem))] = obj
warnings.warn(
"'registerObject' is deprecated and does nothing.",
DeprecationWarning, stacklevel=2
)
def __init__(self, clickRadius=2, moveDistance=5, parent=None):
QtGui.QGraphicsScene.__init__(self, parent)
self.setClickRadius(clickRadius)
......@@ -368,46 +366,15 @@ class GraphicsScene(QtGui.QGraphicsScene):
return ev.isAccepted()
def items(self, *args):
#print 'args:', args
items = QtGui.QGraphicsScene.items(self, *args)
## PyQt bug: items() returns a list of QGraphicsItem instances. If the item is subclassed from QGraphicsObject,
## then the object returned will be different than the actual item that was originally added to the scene
items2 = list(map(self.translateGraphicsItem, items))
#if HAVE_SIP and isinstance(self, sip.wrapper):
#items2 = []
#for i in items:
#addr = sip.unwrapinstance(sip.cast(i, QtGui.QGraphicsItem))
#i2 = GraphicsScene._addressCache.get(addr, i)
##print i, "==>", i2
#items2.append(i2)
#print 'items:', items
return items2
return self.translateGraphicsItems(items)
def selectedItems(self, *args):
items = QtGui.QGraphicsScene.selectedItems(self, *args)
## PyQt bug: items() returns a list of QGraphicsItem instances. If the item is subclassed from QGraphicsObject,
## then the object returned will be different than the actual item that was originally added to the scene
#if HAVE_SIP and isinstance(self, sip.wrapper):
#items2 = []
#for i in items:
#addr = sip.unwrapinstance(sip.cast(i, QtGui.QGraphicsItem))
#i2 = GraphicsScene._addressCache.get(addr, i)
##print i, "==>", i2
#items2.append(i2)
items2 = list(map(self.translateGraphicsItem, items))
#print 'items:', items
return items2
return self.translateGraphicsItems(items)
def itemAt(self, *args):
item = QtGui.QGraphicsScene.itemAt(self, *args)
## PyQt bug: items() returns a list of QGraphicsItem instances. If the item is subclassed from QGraphicsObject,
## then the object returned will be different than the actual item that was originally added to the scene
#if HAVE_SIP and isinstance(self, sip.wrapper):
#addr = sip.unwrapinstance(sip.cast(item, QtGui.QGraphicsItem))
#item = GraphicsScene._addressCache.get(addr, item)
#return item
return self.translateGraphicsItem(item)
def itemsNearEvent(self, event, selMode=QtCore.Qt.IntersectsItemShape, sortOrder=QtCore.Qt.DescendingOrder, hoverable=False):
......@@ -554,10 +521,14 @@ class GraphicsScene(QtGui.QGraphicsScene):
@staticmethod
def translateGraphicsItem(item):
## for fixing pyqt bugs where the wrong item is returned
# This function is intended as a workaround for a problem with older
# versions of PyQt (< 4.9?), where methods returning 'QGraphicsItem *'
# lose the type of the QGraphicsObject subclasses and instead return
# generic QGraphicsItem wrappers.
if HAVE_SIP and isinstance(item, sip.wrapper):
addr = sip.unwrapinstance(sip.cast(item, QtGui.QGraphicsItem))
item = GraphicsScene._addressCache.get(addr, item)
obj = item.toGraphicsObject()
if obj is not None:
item = obj
return item
@staticmethod
......
......@@ -22,8 +22,6 @@ class ExportDialog(QtGui.QWidget):
self.shown = False
self.currentExporter = None
self.scene = scene
self.exporterParameters = {}
self.selectBox = QtGui.QGraphicsRectItem()
self.selectBox.setPen(fn.mkPen('y', width=3, style=QtCore.Qt.DashLine))
......@@ -124,16 +122,7 @@ class ExportDialog(QtGui.QWidget):
expClass = self.exporterClasses[str(item.text())]
exp = expClass(item=self.ui.itemTree.currentItem().gitem)
if prev:
oldtext = str(prev.text())
self.exporterParameters[oldtext] = self.currentExporter.parameters()
newtext = str(item.text())
if newtext in self.exporterParameters.keys():
params = self.exporterParameters[newtext]
exp.params = params
else:
params = exp.parameters()
self.exporterParameters[newtext] = params
params = exp.parameters()
if params is None:
self.ui.paramTree.clear()
......
import numpy as np
class PlotData(object):
......@@ -50,7 +51,3 @@ class PlotData(object):
mn = np.min(self[field])
self.minVals[field] = mn
return mn
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
This module exists to smooth out some of the differences between PySide and PyQt4:
......@@ -9,7 +10,7 @@ This module exists to smooth out some of the differences between PySide and PyQt
"""
import os, sys, re, time
import os, sys, re, time, subprocess, warnings
from .python2_3 import asUnicode
......@@ -104,24 +105,38 @@ def _loadUiType(uiFile):
if QT_LIB == "PYSIDE":
import pysideuic
else:
import pyside2uic as pysideuic
import xml.etree.ElementTree as xml
try:
import pyside2uic as pysideuic
except ImportError:
# later vserions of pyside2 have dropped pysideuic; use the uic binary instead.
pysideuic = None
# get class names from ui file
import xml.etree.ElementTree as xml
parsed = xml.parse(uiFile)
widget_class = parsed.find('widget').get('class')
form_class = parsed.find('class').text
with open(uiFile, 'r') as f:
# convert ui file to python code
if pysideuic is None:
pyside2version = tuple(map(int, PySide2.__version__.split(".")))
if pyside2version >= (5, 14) and pyside2version < (5, 14, 2, 2):
warnings.warn('For UI compilation, it is recommended to upgrade to PySide >= 5.15')
uipy = subprocess.check_output(['pyside2-uic', uiFile])
else:
o = _StringIO()
frame = {}
with open(uiFile, 'r') as f:
pysideuic.compileUi(f, o, indent=0)
uipy = o.getvalue()
pysideuic.compileUi(f, o, indent=0)
pyc = compile(o.getvalue(), '<string>', 'exec')
exec(pyc, frame)
# exceute python code
pyc = compile(uipy, '<string>', 'exec')
frame = {}
exec(pyc, frame)
#Fetch the base_class and form class based on their type in the xml from designer
form_class = frame['Ui_%s'%form_class]
base_class = eval('QtGui.%s'%widget_class)
# fetch the base_class and form class based on their type in the xml from designer
form_class = frame['Ui_%s'%form_class]
base_class = eval('QtGui.%s'%widget_class)
return form_class, base_class
......@@ -329,9 +344,19 @@ if m is not None and list(map(int, m.groups())) < versionReq:
QAPP = None
def mkQApp():
global QAPP
def mkQApp(name=None):
"""
Creates new QApplication or returns current instance if existing.
============== ========================================================
**Arguments:**
name (str) Application name, passed to Qt
============== ========================================================
"""
global QAPP
QAPP = QtGui.QApplication.instance()
if QAPP is None:
QAPP = QtGui.QApplication([])
QAPP = QtGui.QApplication(sys.argv or ["pyqtgraph"])
if name is not None:
QAPP.setApplicationName(name)
return QAPP
......@@ -4,7 +4,7 @@ PyQtGraph - Scientific Graphics and GUI Library for Python
www.pyqtgraph.org
"""
__version__ = '0.11.0rc0'
__version__ = '0.11.0'
### import all the goodies and add some helper functions for easy CLI use
......@@ -219,6 +219,7 @@ from .graphicsItems.ViewBox import *
from .graphicsItems.ArrowItem import *
from .graphicsItems.ImageItem import *
from .graphicsItems.AxisItem import *
from .graphicsItems.DateAxisItem import *
from .graphicsItems.LabelItem import *
from .graphicsItems.CurvePoint import *
from .graphicsItems.GraphicsWidgetAnchor import *
......@@ -412,12 +413,20 @@ def plot(*args, **kargs):
dataArgs[k] = kargs[k]
w = PlotWindow(**pwArgs)
w.sigClosed.connect(_plotWindowClosed)
if len(args) > 0 or len(dataArgs) > 0:
w.plot(*args, **dataArgs)
plots.append(w)
w.show()
return w