Giter Site home page Giter Site logo

db-style-manager's People

Contributors

gustry avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar  avatar

db-style-manager's Issues

Qgis3 Support

Great Work !!!

I can try making it work with qgis3, maybe a qgis3 Branch ? Can I submit a PR ?

Save all layer styles to database as default

Hey @Gustry! I just found your plugin. I was kinda hoping it would do what the title of this ticket suggests - iterate over all my project layers, saving the style of each layer as the default in the PostgreSQL database.

DB Style Manager - Oracle

On the "Load a Style Summary from PostgreSQL" button, could we have one for "Loading a style summary from Oracle" that does the same thing, but over an Oracle connection?

Crash in QGIS 3.16.1

Hi,
I am unable to run the plugin. Immediatly after install, an error is raised :

Impossible de charger l'extension 'db-style-manager' provoque une erreur lors de l'appel à sa méthode classFactory()

TypeError: 'QVariant' object is not subscriptable
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/qgis/utils.py", line 334, in _startPlugin
plugins[packageName] = package.classFactory(iface)
File "/home/jb/.local/share/QGIS/QGIS3/profiles/default/python/plugins/db-style-manager/init.py", line 34, in classFactory
return DbStyleManager(iface)
File "/home/jb/.local/share/QGIS/QGIS3/profiles/default/python/plugins/db-style-manager/plugin.py", line 61, in init
locale = QSettings().value('locale/userLocale')[0:2]
TypeError: 'QVariant' object is not subscriptable

Version de Python : 3.8.5 (default, Jul 28 2020, 12:59:40) [GCC 9.3.0]
Version de QGIS : 3.16.1-Hannover Hannover, b381a90dca

Chemin Python :
/usr/share/qgis/python
/home/jb/.local/share/QGIS/QGIS3/profiles/default/python
/home/jb/.local/share/QGIS/QGIS3/profiles/default/python/plugins
/usr/share/qgis/python/plugins
/usr/lib/python38.zip
/usr/lib/python3.8
/usr/lib/python3.8/lib-dynload
/home/jb/.local/lib/python3.8/site-packages
/usr/local/lib/python3.8/dist-packages
/usr/lib/python3/dist-packages
/usr/lib/python3.8/dist-packages
/home/jb/.local/share/QGIS/QGIS3/profiles/default/python
/mnt/20fcc09a-8a1d-4cfb-bd76-916b293d8d21/jb

Exporter tous les styles

Est-ce possible d'ajouter une fonctionnalité qui fait un export de tous les fichiers XML de la base ?

default style in qgis3

I think there's a small bug in the code.

The problem is in the load_style_from_database(self, layer) function, starting from row #368:

369 manager = layer.styleManager()
370 existing_styles = manager.styles()
371 for s in existing_styles:
372     manager.removeStyle(s)
373 
374 manager.currentStyle()
375 manager.renameStyle(manager.currentStyle(), '')
376 manager.renameStyle('', 'default')
377 styles = layer.listStylesInDatabase()
378 if len(styles) == 0:
379     # No style for all layers in the database, we do nothing
380     return
381 
382 number_styles = styles[0]
383 if number_styles == 0:
384     # No style for this layer in the database, we do nothing
385     return
386 
387 
388 related_styles_idx = styles[1][0:number_styles]
389 related_styles_names = styles[2][0:number_styles]
390 related_styles_description = styles[3][0:number_styles]
391 related_styles = zip(related_styles_idx, related_styles_names, related_styles_description)
392 for style in related_styles:
393     if Qgis.QGIS_VERSION_INT < 30000:
394         xml_style = layer.getStyleFromDatabase(style[0], '')
395     else:
396         xml_style = layer.getStyleFromDatabase(style[0])[0]
397     # description = style[2]
398     manager.addStyle(style[1], QgsMapLayerStyle(xml_style))
399 
400 # Deactivated in 0.3, because in QGIS 2.18 we can't know which one is the default style
401 # if len(number_styles) > 0:
402 #     # If we have at least one style, we take the first one for the title and name
403 #     layer.setTitle(related_styles[0][2])
404 #     layer.setName(related_styles[0][2])
405 
406 # len(zip object) do not exist on Python 3
407 if len(list(related_styles)) >= 1:
408     # We got one layer, we can set it by default in QGIS
409     manager.setCurrentStyle(related_styles[0][1])
410     manager.removeStyle('default')
411 
412     self.iface.messageBar().pushInfo(
413         tr('Style Loaded'),
414         tr('{layer_name} has {number} styles loaded successfully.').format(
415             layer_name=layer.name(), number=len(list(related_styles))))

If I already have got styles associated to the layer (like when I reopen the project and the Load styles automatically button is toggled), it removes all but the last one.

>>>from qgis.core import Qgis
>>>from qgis.PyQt.QtCore import QSettings, QTranslator, qVersion, QCoreApplication
>>>from qgis.core import QgsMapLayer, QgsVectorLayer, QgsMapLayerStyle, QgsProject
>>>from qgis.PyQt.QtGui import QIcon

>>>layer = iface.activeLayer()
layer
<qgis._core.QgsVectorLayer object at 0x000001E88E636CA8>

>>>manager = layer.styleManager()
>>>manager.styles()
['ALGHERO', 'DMA', 'HIDI_DMA', 'Leakage_Index', 'Leakage_Index_via', 'MAT+VALVE', 'MURAVERA', 'OLBIA', 'OZIERI', 'PLOAGHE', 'PORTO TORRES', 'QSE', 'SASSARI', 'SERBATOIO', 'SORSO', 'STG', 'default', 'idx_sost_leakage']
>>>existing_styles = manager.styles()
>>>for s in existing_styles:
...    manager.removeStyle(s)
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
False

>>>manager.styles()
['idx_sost_leakage']

That code leaves the last saved style (not ever the default one). In fact it is confirmed by the definition of the removeStyle() function:

https://qgis.org/api/classQgsMapLayerStyleManager.html#a23ab0b557dc0c1ad6a12e1d17798ba63

removeStyle()
bool QgsMapLayerStyleManager::removeStyle	(	const QString & 	name	)	
Remove a stored style.
Returns
true on success (style exists and it is not the last one)
Definition at line 128 of file qgsmaplayerstylemanager.cpp.

The first problem is that this last style is then renamed as 'default':

>>>manager.currentStyle()
'idx_sost_leakage'
>>>manager.renameStyle(manager.currentStyle(), '')
True
>>>manager.renameStyle('', 'default')
True

The second problem is that it stays there "as is" when the code goes on to retrieve the styles from the database:

>>>styles = layer.listStylesInDatabase()

>>>styles[0]
18
>>>number_styles = styles[0]

>>>styles[1][0:number_styles]
['27', '93', '92', '91', '89', '87', '84', '82', '77', '74', '70', '66', '64', '62', '48', '44', '29', '28']
>>>related_styles_idx = styles[1][0:number_styles]
>>>styles[2][0:number_styles]
['default', 'idx_sost_leakage', 'Leakage_Index_via', 'STG', 'Leakage_Index', 'OLBIA', 'PLOAGHE', 'SASSARI', 'QSE', 'MURAVERA', 'PORTO TORRES', 'SORSO', 'OZIERI', 'ALGHERO', 'MAT+VALVE', 'HIDI_DMA', 'DMA', 'SERBATOIO']
>>>related_styles_names = styles[2][0:number_styles]
>>>styles[3][0:number_styles]
['ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE', 'ACQ_RETE']
>>>related_styles_description = styles[3][0:number_styles]
>>>related_styles = zip(related_styles_idx, related_styles_names, related_styles_description)
>>>related_styles
<zip object at 0x000001E886AD8548>
>>>list(related_styles)
[('27', 'default', 'ACQ_RETE'), ('93', 'idx_sost_leakage', 'ACQ_RETE'), ('92', 'Leakage_Index_via', 'ACQ_RETE'), ('91', 'STG', 'ACQ_RETE'), ('89', 'Leakage_Index', 'ACQ_RETE'), ('87', 'OLBIA', 'ACQ_RETE'), ('84', 'PLOAGHE', 'ACQ_RETE'), ('82', 'SASSARI', 'ACQ_RETE'), ('77', 'QSE', 'ACQ_RETE'), ('74', 'MURAVERA', 'ACQ_RETE'), ('70', 'PORTO TORRES', 'ACQ_RETE'), ('66', 'SORSO', 'ACQ_RETE'), ('64', 'OZIERI', 'ACQ_RETE'), ('62', 'ALGHERO', 'ACQ_RETE'), ('48', 'MAT+VALVE', 'ACQ_RETE'), ('44', 'HIDI_DMA', 'ACQ_RETE'), ('29', 'DMA', 'ACQ_RETE'), ('28', 'SERBATOIO', 'ACQ_RETE')]

>>>for style in related_styles:
>>>		xml_style = layer.getStyleFromDatabase(style[0])[0]
>>>		manager.addStyle(style[1], QgsMapLayerStyle(xml_style))
False
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True

According to me one third problem (not of the plugin) is that QGIS re-sorts the added styles in the style selection popup and in the project file according to the default Python sorting, i.e. taking care of the case (uppercase first and then lowercase). Well, it isn't a big problem, but I would like to sort my layers as I want...:

>>>related_styles = zip(related_styles_idx, related_styles_names, related_styles_description)
>>>list(related_styles)
[('27', 'default', 'ACQ_RETE'), ('93', 'idx_sost_leakage', 'ACQ_RETE'), ('92', 'Leakage_Index_via', 'ACQ_RETE'), ('91', 'STG', 'ACQ_RETE'), ('89', 'Leakage_Index', 'ACQ_RETE'), ('87', 'OLBIA', 'ACQ_RETE'), ('84', 'PLOAGHE', 'ACQ_RETE'), ('82', 'SASSARI', 'ACQ_RETE'), ('77', 'QSE', 'ACQ_RETE'), ('74', 'MURAVERA', 'ACQ_RETE'), ('70', 'PORTO TORRES', 'ACQ_RETE'), ('66', 'SORSO', 'ACQ_RETE'), ('64', 'OZIERI', 'ACQ_RETE'), ('62', 'ALGHERO', 'ACQ_RETE'), ('48', 'MAT+VALVE', 'ACQ_RETE'), ('44', 'HIDI_DMA', 'ACQ_RETE'), ('29', 'DMA', 'ACQ_RETE'), ('28', 'SERBATOIO', 'ACQ_RETE')]

# note that in python3 the iteration cancels the zip :-(
>>>list(related_styles)
[]
>>>for style in related_styles:
...		print(style)
('27', 'default', 'ACQ_RETE')
('93', 'idx_sost_leakage', 'ACQ_RETE')
('92', 'Leakage_Index_via', 'ACQ_RETE')
('91', 'STG', 'ACQ_RETE')
('89', 'Leakage_Index', 'ACQ_RETE')
('87', 'OLBIA', 'ACQ_RETE')
('84', 'PLOAGHE', 'ACQ_RETE')
('82', 'SASSARI', 'ACQ_RETE')
('77', 'QSE', 'ACQ_RETE')
('74', 'MURAVERA', 'ACQ_RETE')
('70', 'PORTO TORRES', 'ACQ_RETE')
('66', 'SORSO', 'ACQ_RETE')
('64', 'OZIERI', 'ACQ_RETE')
('62', 'ALGHERO', 'ACQ_RETE')
('48', 'MAT+VALVE', 'ACQ_RETE')
('44', 'HIDI_DMA', 'ACQ_RETE')
('29', 'DMA', 'ACQ_RETE')
('28', 'SERBATOIO', 'ACQ_RETE')

>>>manager.styles()
['ALGHERO', 'DMA', 'HIDI_DMA', 'Leakage_Index', 'Leakage_Index_via', 'MAT+VALVE', 'MURAVERA', 'OLBIA', 'OZIERI', 'PLOAGHE', 'PORTO TORRES', 'QSE', 'SASSARI', 'SERBATOIO', 'SORSO', 'STG', 'default', 'idx_sost_leakage']

One workaround for the first problem can be a "selective" style removing in line #371, but if the user has changed the 'default' style it will stay the same also after the style reset:

>>>for s in (x for x in existing_styles if x!='default'):
...		manager.removeStyle(s)
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True

>>>manager.styles()
['default']

A second (and I think more valid) workaround can be:

  1. move the conditionals from line #377 to line #385 at the beginning of the function (do nothing in case there aren't valid styles in the database)
  2. after deletion, rename the "remaining" style to 'garbage' in line #375;
  3. not renaming to 'default' in line #376;
  4. check if there's a new loaded style called 'default' and making it the current one;
  5. remove the 'garbage' style.

The function code will change in the following manner:

    def load_style_from_database(self, layer):
    
        styles = layer.listStylesInDatabase()
        if len(styles) == 0:
            # No style for all layers in the database, we do nothing
            return

        number_styles = styles[0]
        if number_styles == 0:
            # No style for this layer in the database, we do nothing
            return
            
        manager = layer.styleManager()
        existing_styles = manager.styles()
        for s in existing_styles:
            manager.removeStyle(s)

        manager.currentStyle()
        manager.renameStyle(manager.currentStyle(), 'garbage')
        # manager.renameStyle('', 'default')

        related_styles_idx = styles[1][0:number_styles]
        related_styles_names = styles[2][0:number_styles]
        related_styles_description = styles[3][0:number_styles]
        related_styles = zip(related_styles_idx, related_styles_names, related_styles_description)
        for style in related_styles:
            if Qgis.QGIS_VERSION_INT < 30000:
                xml_style = layer.getStyleFromDatabase(style[0], '')
            else:
                xml_style = layer.getStyleFromDatabase(style[0])[0]
            # description = style[2]
            manager.addStyle(style[1], QgsMapLayerStyle(xml_style))
        
        loaded_styles = manager.styles()
        for s in (x for x in loaded_styles if x=='default'):
            manager.setCurrentStyle(s)
        
        manager.removeStyle('garbage')
        
        # Deactivated in 0.3, because in QGIS 2.18 we can't know which one is the default style
        # if len(number_styles) > 0:
        #     # If we have at least one style, we take the first one for the title and name
        #     layer.setTitle(related_styles[0][2])
        #     layer.setName(related_styles[0][2])

        # len(zip object) do not exist on Python 3
        if len(list(related_styles)) >= 1:
            # We got one layer, we can set it by default in QGIS
            manager.setCurrentStyle(related_styles[0][1])
            manager.removeStyle('default')

            self.iface.messageBar().pushInfo(
                tr('Style Loaded'),
                tr('{layer_name} has {number} styles loaded successfully.').format(
                    layer_name=layer.name(), number=len(list(related_styles))))

As said some lines up, in python3 any iteration (or listing) cancels the contents of the zip so the code from lines #406 to #415 won't work ( len(list(related_styles)) = 0 after the iteration).

The remaining problem is that if a user has created a new style and, after that, he wants to load also the styles from the db, the procedure will erase his newly created style (if he hasn't saved it in the db)...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.