diff --git a/example/dynamicList/main.py b/example/dynamicList/main.py index 837db42..b6eaefd 100644 --- a/example/dynamicList/main.py +++ b/example/dynamicList/main.py @@ -1,8 +1,5 @@ import sys -sys.path.append("../..") - from quickmamba.models import QObjectListModel - from PyQt5 import QtCore, QtWidgets, QtQuick diff --git a/example/dynamicRepeater/main.py b/example/dynamicRepeater/main.py index ed62feb..f901fb2 100644 --- a/example/dynamicRepeater/main.py +++ b/example/dynamicRepeater/main.py @@ -1,8 +1,6 @@ import sys -sys.path.append("../..") - -from PyQt5 import QtCore, QtWidgets, QtQuick from quickmamba.models import QObjectListModel +from PyQt5 import QtCore, QtWidgets, QtQuick class ClipWrapper(QtCore.QObject): diff --git a/example/instantCoding/main.py b/example/instantCoding/main.py index d53f990..76a3bcd 100644 --- a/example/instantCoding/main.py +++ b/example/instantCoding/main.py @@ -1,10 +1,6 @@ import sys -sys.path.append("../..") - -from quickmamba.utils import QmlInstantCoding - -from PyQt5 import QtCore, QtWidgets, QtQuick - +from quickmamba.utils.instantcoding import QmlInstantCoding, ReloadComponent +from PyQt5 import QtCore, QtWidgets, QtQuick, QtQml import os @@ -14,9 +10,14 @@ def main(): view.setResizeMode(QtQuick.QQuickView.SizeRootObjectToView) # Create a declarative view - view.setSource(QtCore.QUrl("source.qml")) + component = QtQml.QQmlComponent(view) + topLevelItem = component.create() + qmlFile = QtCore.QUrl("source.qml") + view.setSource(qmlFile) + # Declare we are using instant coding tool on this view - qic = QmlInstantCoding(view, verbose=True) + qic = QmlInstantCoding(view, ReloadComponent(qmlFile, component, topLevelItem), verbose=True) + # Add any source file (.qml and .js by default) in current working directory qic.addFilesFromDirectory(os.getcwd()) diff --git a/example/screenPicker/main.py b/example/screenPicker/main.py index 485d662..56e03c1 100644 --- a/example/screenPicker/main.py +++ b/example/screenPicker/main.py @@ -1,17 +1,9 @@ import sys from PyQt5 import QtCore, QtWidgets, QtQuick -#Fix execution shader bug -from OpenGL import GL - - -import os - -currentFilePath = os.path.dirname(os.path.abspath(__file__)) -quickmambaPath = os.path.join(currentFilePath, '../QuickMamba') -sys.path.append(quickmambaPath) - +from OpenGL import GL # Fix execution shader bug import quickmamba + if __name__ == '__main__': quickmamba.qmlRegister() app = QtWidgets.QApplication(sys.argv) diff --git a/example/signal/main.py b/example/signal/main.py index f2ef757..6eb75a7 100644 --- a/example/signal/main.py +++ b/example/signal/main.py @@ -1,8 +1,6 @@ -import sys -sys.path.append("../..") - from quickmamba.patterns import Signal + # Sample usage: class Model(object): def __init__(self, value): @@ -11,7 +9,7 @@ def __init__(self, value): def set_value(self, value): self.__value = value - self.changed() # Emit signal + self.changed() # Emit signal def get_value(self): return self.__value diff --git a/quickmamba/gui/__init__.py b/quickmamba/gui/__init__.py index 2bee7a9..6bd787b 100644 --- a/quickmamba/gui/__init__.py +++ b/quickmamba/gui/__init__.py @@ -5,6 +5,7 @@ from PyQt5 import QtQml + def qmlRegister(): QtQml.qmlRegisterType(WheelArea, "QuickMamba", 1, 0, "WheelAreaImpl") QtQml.qmlRegisterType(ExternDropArea, "QuickMamba", 1, 0, "ExternDropAreaImpl") diff --git a/quickmamba/gui/colorPicker.py b/quickmamba/gui/colorPicker.py index 464dacd..bf3c96b 100644 --- a/quickmamba/gui/colorPicker.py +++ b/quickmamba/gui/colorPicker.py @@ -3,15 +3,16 @@ from PyQt5 import QtQuick from PyQt5.QtGui import QCursor, QColor, QGuiApplication + class ColorPickingEventFilter(QtCore.QObject): def __init__(self, screenpicker): QtCore.QObject.__init__(self, screenpicker) self._screenpicker = screenpicker def eventFilter(self, obj, event): - if(event.type() == QtCore.QEvent.MouseMove): + if event.type() == QtCore.QEvent.MouseMove: self._screenpicker.updateCurrentColor() - elif(event.type() == QtCore.QEvent.MouseButtonRelease): + elif event.type() == QtCore.QEvent.MouseButtonRelease: self._screenpicker.setGrabbing(False) self._screenpicker.accepted.emit() @@ -20,20 +21,16 @@ def eventFilter(self, obj, event): class ColorPicker(QtQuick.QQuickItem): """ - Define the common methods and fields for screenPicker. + Define the common methods and fields for screenPicker. """ - def __init__(self, parent = None): + def __init__(self, parent=None): QtQuick.QQuickItem.__init__(self, parent) self._currentColor = QColor("#FFFFFF") self._grabbing = False self._desktop = QtWidgets.QDesktopWidget() self._colorPickingEventFilter = ColorPickingEventFilter(self) - # ######################################## Methods private to this class ####################################### # - - # ## Getters ## # - def getCurrentColor(self): return self._currentColor @@ -45,19 +42,19 @@ def isGrabbing(self): return self._grabbing def setGrabbing(self, grabbing): - if(self._grabbing != grabbing): - self._grabbing = grabbing - if(self._grabbing): - self.installEventFilter(self._colorPickingEventFilter) - QGuiApplication.setOverrideCursor(QCursor(QtCore.Qt.CrossCursor)) - self.grabMouse() - else: - self.ungrabMouse() - self.removeEventFilter(self._colorPickingEventFilter) - QGuiApplication.restoreOverrideCursor() - self.grabbingChanged.emit() - - # ## Others ## # + if self._grabbing == grabbing: + return + + self._grabbing = grabbing + if self._grabbing: + self.installEventFilter(self._colorPickingEventFilter) + QGuiApplication.setOverrideCursor(QCursor(QtCore.Qt.CrossCursor)) + self.grabMouse() + else: + self.ungrabMouse() + self.removeEventFilter(self._colorPickingEventFilter) + QGuiApplication.restoreOverrideCursor() + self.grabbingChanged.emit() def updateCurrentColor(self): cursorPos = QCursor.pos() @@ -67,12 +64,8 @@ def updateCurrentColor(self): qColor = QColor(qImage.pixel(0, 0)) self.setCurrentColor(qColor) -# ############################################# Data exposed to QML ############################################## # - accepted = QtCore.pyqtSignal() - currentColorChanged = QtCore.pyqtSignal() currentColor = QtCore.pyqtProperty(QColor, getCurrentColor, setCurrentColor, notify=currentColorChanged) - grabbingChanged = QtCore.pyqtSignal() grabbing = QtCore.pyqtProperty(bool, isGrabbing, setGrabbing, notify=grabbingChanged) \ No newline at end of file diff --git a/quickmamba/gui/externDropArea.py b/quickmamba/gui/externDropArea.py index bb22ead..37d753b 100644 --- a/quickmamba/gui/externDropArea.py +++ b/quickmamba/gui/externDropArea.py @@ -1,51 +1,53 @@ from PyQt5 import QtCore, QtGui from PyQt5 import QtQuick + class ExternDropArea(QtQuick.QQuickItem): # QGraphicsSceneDragDropEvent: - #Qt::MouseButtons buttons, Qt::DropAction dropAction, const QMimeData mimeData, Qt::KeyboardModifiers modifiers, QPointF pos, Qt::DropActions possibleActions, Qt::DropAction proposedAction, QWidget source + # Qt::MouseButtons buttons, Qt::DropAction dropAction, + # const QMimeData mimeData, Qt::KeyboardModifiers modifiers, + # QPointF pos, Qt::DropActions possibleActions, Qt::DropAction proposedAction, QWidget source internDragEnter = QtCore.pyqtSignal( - bool, str, # text - bool, str, # html - bool, str, # urls + bool, str, # text + bool, str, # html + bool, str, # urls QtCore.Qt.MouseButtons, QtCore.Qt.DropAction, QtCore.Qt.KeyboardModifiers, QtCore.QPointF, QtCore.Qt.DropActions, QtCore.Qt.DropAction, str) internDragMove = QtCore.pyqtSignal( - bool, str, # text - bool, str, # html - bool, str, # urls + bool, str, # text + bool, str, # html + bool, str, # urls QtCore.Qt.MouseButtons, QtCore.Qt.DropAction, QtCore.Qt.KeyboardModifiers, QtCore.QPointF, QtCore.Qt.DropActions, QtCore.Qt.DropAction, str) internDragLeave = QtCore.pyqtSignal( - bool, str, # text - bool, str, # html - bool, str, # urls + bool, str, # text + bool, str, # html + bool, str, # urls QtCore.Qt.MouseButtons, QtCore.Qt.DropAction, QtCore.Qt.KeyboardModifiers, QtCore.QPointF, QtCore.Qt.DropActions, QtCore.Qt.DropAction, str) internDrop = QtCore.pyqtSignal( - bool, str, # text - bool, str, # html - bool, str, # urls + bool, str, # text + bool, str, # html + bool, str, # urls QtCore.Qt.MouseButtons, QtCore.Qt.DropAction, QtCore.Qt.KeyboardModifiers, QtCore.QPointF, QtCore.Qt.DropActions, QtCore.Qt.DropAction, str) - def __init__(self, parent = None): + def __init__(self, parent=None): super(ExternDropArea, self).__init__(parent) #self.setAcceptDrops(True) def dragEnterEvent(self, event): - #print 'dragEnterEvent' - urls = event.mimeData().urls() - firstUrl = urls[0].toLocalFile() if len(urls) else "" + firstUrl = urls[0].toLocalFile() if len(urls) else '' \ + '' self.internDragEnter.emit( event.mimeData().hasText(), event.mimeData().text(), event.mimeData().hasHtml(), event.mimeData().html(), @@ -66,10 +68,9 @@ def dragEnterEvent(self, event): event.setAccepted(self._acceptDropValue) def dragMoveEvent(self, event): - #print 'dragMoveEvent' - urls = event.mimeData().urls() - firstUrl = urls[0].toLocalFile() if len(urls) else "" + firstUrl = urls[0].toLocalFile() if len(urls) else '' + self.internDragMove.emit( event.mimeData().hasText(), event.mimeData().text(), event.mimeData().hasHtml(), event.mimeData().html(), @@ -86,10 +87,9 @@ def dragMoveEvent(self, event): event.setAccepted(self._acceptDropValue) def dragLeaveEvent(self, event): - #print 'dragLeaveEvent' - urls = event.mimeData().urls() firstUrl = urls[0].toLocalFile() if len(urls) else "" + self.internDragLeave.emit( event.mimeData().hasText(), event.mimeData().text(), event.mimeData().hasHtml(), event.mimeData().html(), @@ -107,8 +107,6 @@ def dragLeaveEvent(self, event): self.unsetCursor() def dropEvent(self, event): - #print 'dropEvent' - # hasText() text() text/plain # hasHtml() html() text/html # hasUrls() urls() text/uri-list @@ -134,7 +132,6 @@ def dropEvent(self, event): event.possibleActions(), event.proposedAction(), "") #event.source().accessibleName() if event.source() else "") - if self._acceptDropValue: event.acceptProposedAction() @@ -154,7 +151,6 @@ def setAcceptDrop(self, acceptDrop): self.acceptDropChanged.emit() acceptDropChanged = QtCore.pyqtSignal() - _acceptDropValue = True acceptDrop = QtCore.pyqtProperty(bool, getAcceptDrop, setAcceptDrop, notify=acceptDropChanged) diff --git a/quickmamba/gui/wheelArea.py b/quickmamba/gui/wheelArea.py index 7c0a8a7..76d7211 100644 --- a/quickmamba/gui/wheelArea.py +++ b/quickmamba/gui/wheelArea.py @@ -1,12 +1,13 @@ from PyQt5 import QtCore from PyQt5 import QtQuick + class WheelArea(QtQuick.QQuickItem): - #QPointF pos, int delta, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers + # QPointF pos, int delta, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers internVerticalWheel = QtCore.pyqtSignal(QtCore.QPointF, int, QtCore.Qt.MouseButtons, QtCore.Qt.KeyboardModifiers) internHorizontalWheel = QtCore.pyqtSignal(QtCore.QPointF, int, QtCore.Qt.MouseButtons, QtCore.Qt.KeyboardModifiers) - def __init__(self, parent = None): + def __init__(self, parent=None): QtQuick.QQuickItem.__init__(self, parent) def wheelEvent(self, event): diff --git a/quickmamba/models/listmodel.py b/quickmamba/models/listmodel.py index db1c158..7eb4414 100644 --- a/quickmamba/models/listmodel.py +++ b/quickmamba/models/listmodel.py @@ -12,19 +12,23 @@ class QObjectListModel(QtCore.QAbstractListModel): This class is the Python port of the C++ QObjectListModel class. """ def __init__(self, parent=None): - """ Constructs an object list model with the given parent. """ + """ + Constructs an object list model with the given parent. + """ super(QObjectListModel, self).__init__(parent) self._objects = list() # Internal list of objects self.roles = QtCore.QAbstractListModel.roleNames(self) self.ObjectRole = QtCore.Qt.UserRole + 1 - self.roles[self.ObjectRole] = b"object" + self.roles[self.ObjectRole] = b'object' def roleNames(self): return self.roles def __iter__(self): - """ Enables iteration over the list of objects. """ + """ + Enables iteration over the list of objects. + """ return iter(self._objects) def __len__(self): @@ -34,11 +38,14 @@ def __bool__(self): return self.size() > 0 def __getitem__(self, index): - """ Enables the [] operator """ + """ + Enables the [] operator + """ return self._objects[index] def data(self, index, role): - """ Returns data for the specified role, from the item with the + """ + Returns data for the specified role, from the item with the given index. The only valid role is ObjectRole. If the view requests an invalid index or role, an invalid variant @@ -53,17 +60,21 @@ def data(self, index, role): return None def rowCount(self, parent): - """ Returns the number of rows in the model. This value corresponds to the + """ + Returns the number of rows in the model. This value corresponds to the number of items in the model's internal object list. """ return self.size() def objectList(self): - """ Returns the object list used by the model to store data. """ + """ + Returns the object list used by the model to store data. + """ return self._objects def setObjectList(self, objects): - """ Sets the model's internal objects list to objects. The model will + """ + Sets the model's internal objects list to objects. The model will notify any attached views that its underlying data has changed. """ oldSize = self.size() @@ -71,6 +82,7 @@ def setObjectList(self, objects): self._objects = objects self.endResetModel() self.dataChanged.emit(self.index(0), self.index(self.size() - 1), []) + if self.size() != oldSize: self.countChanged.emit() @@ -78,36 +90,44 @@ def setObjectList(self, objects): # List API # ############ def append(self, toAppend): - """ Inserts object(s) at the end of the model and notifies any views. + """ + Inserts object(s) at the end of the model and notifies any views. Accepts both QObject and list of QObjects. """ if not isinstance(toAppend, list): toAppend = [toAppend] + self.beginInsertRows(QtCore.QModelIndex(), self.size(), self.size() + len(toAppend) - 1) self._objects.extend(toAppend) self.endInsertRows() self.countChanged.emit() def insert(self, i, toInsert): - """ Inserts object(s) at index position i in the model and notifies - any views. If i is 0, the object is prepended to the model. If i - is size(), the object is appended to the list. + """ + Inserts object(s) at index position i in the model and notifies + any views. If i is 0, the object is prepended to the model. + If is size(), the object is appended to the list. Accepts both QObject and list of QObjects. """ if not isinstance(toInsert, list): toInsert = [toInsert] self.beginInsertRows(QtCore.QModelIndex(), i, i + len(toInsert) - 1) + for obj in reversed(toInsert): self._objects.insert(i, obj) + self.endInsertRows() self.countChanged.emit() def at(self, i): - """ Use [] instead - Return the object at index i. """ + """ + Use [] instead - Return the object at index i. + """ return self._objects[i] def replace(self, i, obj): - """ Replaces the item at index position i with object and + """ + Replaces the item at index position i with object and notifies any views. i must be a valid index position in the list (i.e., 0 <= i < size()). """ @@ -115,11 +135,10 @@ def replace(self, i, obj): self.dataChanged.emit(self.index(i), self.index(i), []) def move(self, fromIndex, toIndex): - """ Moves the item at index position from to index position to - and notifies any views. - This function assumes that both from and to are at least 0 but less than - size(). To avoid failure, test that both from and to are at - least 0 and less than size(). + """ + Moves the item at index position from to index position to and notifies any views. + This function assumes that both from and to are at least 0 but less than size(). + To avoid failure, test that both from and to are at least 0 and less than size(). """ value = toIndex if toIndex > fromIndex: @@ -130,9 +149,9 @@ def move(self, fromIndex, toIndex): self.endMoveRows() def removeAt(self, i, count=1): - """ Removes count number of items from index position i and notifies any views. - i must be a valid index position in the model (i.e., 0 <= i < size()), as - must as i + count - 1. + """ + Removes count number of items from index position i and notifies any views. + i must be a valid index position in the model (i.e., 0 <= i < size()), as must as i + count - 1. """ self.beginRemoveRows(QtCore.QModelIndex(), i, i + count - 1) for cpt in range(count): @@ -141,13 +160,16 @@ def removeAt(self, i, count=1): self.countChanged.emit() def remove(self, obj): - """ Removes the first occurrence of the given object. Raises a ValueError if not in list. """ + """ + Removes the first occurrence of the given object. Raises a ValueError if not in list. + """ if not self.contains(obj): raise ValueError("QObjectListModel.remove(obj) : obj not in list") self.removeAt(self.indexOf(obj)) def takeAt(self, i): - """ Removes the item at index position i (notifying any views) and returns it. + """ + Removes the item at index position i (notifying any views) and returns it. i must be a valid index position in the model (i.e., 0 <= i < size()). """ self.beginRemoveRows(QtCore.QModelIndex(), i, i) @@ -157,7 +179,9 @@ def takeAt(self, i): return obj def clear(self): - """ Removes all items from the model and notifies any views. """ + """ + Removes all items from the model and notifies any views. + """ if not self._objects: return self.beginRemoveRows(QtCore.QModelIndex(), 0, self.size() - 1) @@ -166,13 +190,15 @@ def clear(self): self.countChanged.emit() def contains(self, obj): - """ Returns true if the list contains an occurrence of object; + """ + Returns true if the list contains an occurrence of object; otherwise returns false. """ return obj in self._objects def indexOf(self, matchObj, fromIndex=0, positive=True): - """ Returns the index position of the first occurrence of object in + """ + Returns the index position of the first occurrence of object in the model, searching forward from index position from. If positive is True, will always return a positive index. """ @@ -182,9 +208,9 @@ def indexOf(self, matchObj, fromIndex=0, positive=True): return index def lastIndexOf(self, matchObj, fromIndex=-1, positive=True): - """ Returns the index position of the last occurrence of object in - the list, searching backward from index position from. If - from is -1 (the default), the search starts at the last item. + """ + Returns the index position of the last occurrence of object in the list, searching backward from index position from. + If from is -1 (the default), the search starts at the last item. If positive is True, will always return a positive index. """ r = list(self._objects) @@ -195,17 +221,22 @@ def lastIndexOf(self, matchObj, fromIndex=-1, positive=True): return index def size(self): - """ Returns the number of items in the model. """ + """ + Returns the number of items in the model. + """ return len(self._objects) @QtCore.pyqtSlot(result=bool) def isEmpty(self): - """ Returns true if the model contains no items; otherwise returns false. """ + """ + Returns true if the model contains no items; otherwise returns false. + """ return len(self._objects) == 0 @QtCore.pyqtSlot(int, result="QVariant") def get(self, i): - """ For usage from QML. + """ + For usage from QML. Note: return param is mandatory to mimic Q_INVOKABLE C++ method behavior """ return self._objects[i] diff --git a/quickmamba/patterns/signalEvent.py b/quickmamba/patterns/signalEvent.py index 98a4701..6d97d8d 100644 --- a/quickmamba/patterns/signalEvent.py +++ b/quickmamba/patterns/signalEvent.py @@ -1,9 +1,10 @@ import weakref + class Signal(object): - ''' + """ Simple signal implementation by Thiago Marcos P. Santos (thanks!). - ''' + """ def __init__(self): self.__slots = weakref.WeakValueDictionary() @@ -23,5 +24,3 @@ def disconnect(self, slot): def clear(self): self.__slots.clear() - - diff --git a/quickmamba/patterns/singleton.py b/quickmamba/patterns/singleton.py index 1de7592..2a2a1cd 100644 --- a/quickmamba/patterns/singleton.py +++ b/quickmamba/patterns/singleton.py @@ -1,7 +1,6 @@ - class Singleton(object): def __new__(type): - if not '_the_instance' in type.__dict__: + if '_the_instance' not in type.__dict__: type._the_instance = object.__new__(type) return type._the_instance diff --git a/quickmamba/utils/instantcoding.py b/quickmamba/utils/instantcoding.py index 0220c9b..5851199 100644 --- a/quickmamba/utils/instantcoding.py +++ b/quickmamba/utils/instantcoding.py @@ -5,10 +5,10 @@ class ReloadComponent: - ''' + """ Functor to reload a QML component. Will destroy and recreate the component. - ''' + """ def __init__(self, qmlFile, component, topLevelItem): self._qmlFile = qmlFile @@ -26,9 +26,9 @@ def __call__(self): class ReloadView: def __init__(self, view): - ''' + """ Functor to reload a QQuickView. - ''' + """ self._view = view def __call__(self): @@ -37,9 +37,9 @@ def __call__(self): class AskQmlItemToReload: - ''' + """ Functor to ask the top QML item to reload all the content. - ''' + """ def __init__(self, topLevelItem): self._topLevelItem = topLevelItem diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000