diff --git a/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt b/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt index 61bc5eb185..4a58aa22f7 100644 --- a/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt +++ b/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt @@ -1,98 +1,99 @@ # Copyright (C) 2012, 2013 Shaheed Haque # Copyright (C) 2013 Alex Turbov # Copyright (C) 2014-2016 Boudewijn Rempt # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. include(CMakeParseArguments) # # Simple helper function to install plugin and related files # having only a name of the plugin... # (just to reduce syntactic noise when a lot of plugins get installed) # function(install_pykrita_plugin name) set(_options) set(_one_value_args) set(_multi_value_args PATTERNS FILE) cmake_parse_arguments(install_pykrita_plugin "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN}) if(NOT name) message(FATAL_ERROR "Plugin filename is not given") endif() if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py) install(FILES kritapykrita_${name}.desktop DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) foreach(_f ${name}.py ${name}.ui ${install_pykrita_plugin_FILE}) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_f}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${_f} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) endif() endforeach() elseif(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${name}) install(FILES ${name}/kritapykrita_${name}.desktop DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) install( DIRECTORY ${name} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "*.py" PATTERN "*.ui" PATTERN "__pycache__*" EXCLUDE ) # TODO Is there any way to form a long PATTERN options string # and use it in a single install() call? # NOTE Install specified patterns one-by-one... foreach(_pattern ${install_pykrita_plugin_PATTERNS}) install( DIRECTORY ${name} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "${_pattern}" PATTERN "__pycache__*" EXCLUDE ) endforeach() else() message(FATAL_ERROR "Do not know what to do with ${name}") endif() endfunction() install_pykrita_plugin(hello) install_pykrita_plugin(assignprofiledialog) install_pykrita_plugin(scripter) #install_pykrita_plugin(highpass) install_pykrita_plugin(tenbrushes) install( FILES tenbrushes/tenbrushes.action DESTINATION ${DATA_INSTALL_DIR}/krita/actions) install_pykrita_plugin(palette_docker) +install_pykrita_plugin(quick_settings_docker) # if(PYTHON_VERSION_MAJOR VERSION_EQUAL 3) # install_pykrita_plugin(cmake_utils) # install_pykrita_plugin(js_utils PATTERNS "*.json") # install_pykrita_plugin(expand PATTERNS "*.expand" "templates/*.tpl") # endif() install( DIRECTORY libkritapykrita DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "*.py" PATTERN "__pycache__*" EXCLUDE ) diff --git a/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/__init__.py b/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/__init__.py new file mode 100644 index 0000000000..0d88fbd7ea --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/__init__.py @@ -0,0 +1,2 @@ + # let's make a module +from .quick_settings_docker import * diff --git a/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/kritapykrita_quick_settings_docker.desktop b/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/kritapykrita_quick_settings_docker.desktop new file mode 100644 index 0000000000..61f8eff94b --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/kritapykrita_quick_settings_docker.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Krita/PythonPlugin +X-KDE-Library=quick_settings_docker +X-Python-2-Compatible=false +Name=Quick Settings Docker +Comment=A Python-based docker for quickly changing brush size and opacity. diff --git a/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/quick_settings_docker.py b/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/quick_settings_docker.py new file mode 100644 index 0000000000..86015be5e8 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/quick_settings_docker/quick_settings_docker.py @@ -0,0 +1,160 @@ +''' +Description: A Python based docker for quickly choosing the brushsize like similar dockers in other drawing programs. + +By Wolthera + +@package quick_settings_docker +''' + +# Importing the relevant dependancies: +import sys +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +from krita import * + +class QuickSettingsDocker(DockWidget): +#Init the docker + def __init__(self): + super().__init__() + # make base-widget and layout + widget = QWidget() + layout = QVBoxLayout() + widget.setLayout(layout) + self.setWindowTitle("Quick Settings Docker") + tabWidget = QTabWidget() + + self.brushSizeTableView = QTableView() + self.brushSizeTableView.verticalHeader().hide() + self.brushSizeTableView.horizontalHeader().hide() + self.brushSizeTableView.setSelectionMode(QTableView.SingleSelection) + + self.brushOpacityTableView = QTableView() + self.brushOpacityTableView.verticalHeader().hide() + self.brushOpacityTableView.horizontalHeader().hide() + self.brushOpacityTableView.setSelectionMode(QTableView.SingleSelection) + + self.brushFlowTableView = QTableView() + self.brushFlowTableView.verticalHeader().hide() + self.brushFlowTableView.horizontalHeader().hide() + self.brushFlowTableView.setSelectionMode(QTableView.SingleSelection) + + tabWidget.addTab(self.brushSizeTableView, "Size") + tabWidget.addTab(self.brushOpacityTableView, "Opacity") + tabWidget.addTab(self.brushFlowTableView, "Flow") + layout.addWidget(tabWidget) + self.setWidget(widget) #Add the widget to the docker. + + #amount of columns in each row for all the tables. + self.columns = 4 + + # We want a grid with possible options to select. + # To do this, we'll make a TableView widget and use a standarditemmodel for the entries. + # The entries are filled out based on the sizes and opacity lists. + + #Sizes and opacity lists. The former is half-way copied from ptsai, the latter is based on personal experience of useful opacities. + self.sizesList = [0.7, 1.0, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100, 120, 160, 200, 250, 300, 350, 400, 450, 500] + self.opacityList = [0.1, 0.5, 1, 5, 10, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100] + self.brushSizeModel = QStandardItemModel( (len(self.sizesList)/self.columns)+1 , self.columns) + self.brushOpacityModel = QStandardItemModel( (len(self.opacityList)/self.columns)+1 , self.columns ) + self.brushFlowModel = QStandardItemModel( (len(self.opacityList)/self.columns)+1 , self.columns ) + self.fillSizesModel() + self.fillOpacityModel() + + # Now we're done filling out our tables, we connect the views to the functions that'll change the settings. + self.brushSizeTableView.clicked.connect(self.setBrushSize) + self.brushOpacityTableView.clicked.connect(self.setBrushOpacity) + self.brushFlowTableView.clicked.connect(self.setBrushFlow) + + + def fillSizesModel(self): + #First we empty the old model. We might wanna use this function in the future to fill it with the brushmask of the selected brush, but there's currently no API for recognising changes in the current brush nor is there a way to get its brushmask. + self.brushSizeModel.clear() + for s in range(len(self.sizesList)): + # we're gonna itterate over our list, and make a new item for each entry. + # We need to disable a bunch of stuff to make sure people won't do funny things to our entries. + item = QStandardItem() + item.setCheckable(False) + item.setEditable(False) + item.setDragEnabled(False) + item.setText(str(self.sizesList[s])) + # And from here on we'll make an icon. + brushImage = QPixmap(32, 32) + img = brushImage.toImage() + circlePainter = QPainter() + img.fill(self.brushSizeTableView.palette().color(QPalette.Base)) + circlePainter.begin(img) + brush = QBrush(Qt.SolidPattern) + brush.setColor(self.brushSizeTableView.palette().color(QPalette.Text)) + circlePainter.setBrush(brush) + circlePainter.pen().setWidth(0) + brushSize = max(min((self.sizesList[s]/500)*100, 32), 1) + brushSize = brushSize*0.5 + circlePainter.drawEllipse(QPointF(16, 16), brushSize, brushSize) + circlePainter.end() + brushImage = QPixmap.fromImage(img) + # now we're done with drawing the icon, so we set it on the item. + item.setIcon(QIcon(brushImage)) + self.brushSizeModel.setItem(s/4, s%4, item) + self.brushSizeTableView.setModel(self.brushSizeModel) + self.brushSizeTableView.resizeColumnsToContents() + + def fillOpacityModel(self): + self.brushOpacityModel.clear() + self.brushFlowModel.clear() + for s in range(len(self.opacityList)): + # we're gonna itterate over our list, and make a new item for each entry. + item = QStandardItem() + item.setCheckable(False) + item.setEditable(False) + item.setDragEnabled(False) + item.setText(str(self.opacityList[s])) + brushImage = QPixmap(32, 32) + img = brushImage.toImage() + circlePainter = QPainter() + img.fill(self.brushSizeTableView.palette().color(QPalette.Base)) + circlePainter.begin(img) + brush = QBrush(Qt.SolidPattern) + brush.setColor(self.brushSizeTableView.palette().color(QPalette.Text)) + circlePainter.setBrush(brush) + circlePainter.pen().setWidth(0) + circlePainter.setOpacity(self.opacityList[s]/100) + circlePainter.drawEllipse(QPointF(16, 16), 16, 16) + circlePainter.end() + brushImage = QPixmap.fromImage(img) + item.setIcon(QIcon(brushImage)) + # the flow and opacity models will use virtually the same items, but Qt would like us to make sure we understand + # these are not really the same items, so hence the clone. + itemFlow = item.clone() + self.brushOpacityModel.setItem(s/4, s%4, item) + self.brushFlowModel.setItem(s/4, s%4, itemFlow) + self.brushOpacityTableView.setModel(self.brushOpacityModel) + self.brushFlowTableView.setModel(self.brushFlowModel) + self.brushFlowTableView.resizeColumnsToContents() + self.brushOpacityTableView.resizeColumnsToContents() + + def canvasChanged(self, canvas): + pass + + @pyqtSlot('QModelIndex') + def setBrushSize(self, index): + i = index.column()+(index.row()*self.columns) + brushSize = self.sizesList[i] + if Application.activeWindow() and len(Application.activeWindow().views()) > 0: + Application.activeWindow().views()[0].setBrushSize(brushSize); + @pyqtSlot('QModelIndex') + def setBrushOpacity(self, index): + i = index.column()+(index.row()*self.columns) + brushOpacity = self.opacityList[i]/100 + if Application.activeWindow() and len(Application.activeWindow().views()) > 0: + Application.activeWindow().views()[0].setPaintingOpacity(brushOpacity); + @pyqtSlot('QModelIndex') + def setBrushFlow(self, index): + i = index.column()+(index.row()*self.columns) + brushOpacity = self.opacityList[i]/100 + if Application.activeWindow() and len(Application.activeWindow().views()) > 0: + Application.activeWindow().views()[0].setPaintingFlow(brushOpacity); + + +#Add docker to the application :) +Application.addDockWidgetFactory(DockWidgetFactory("quick_settings_docker", DockWidgetFactoryBase.DockRight, QuickSettingsDocker)) +