首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通过将目录中的文件与已加载的文件匹配来更新GIS项目,并应用相关符号

通过将目录中的文件与已加载的文件匹配来更新GIS项目,并应用相关符号
EN

Code Review用户
提问于 2017-08-21 10:57:26
回答 1查看 89关注 0票数 6

我正在为地理信息系统( GIS )软件QGIS开发一个插件,它使用Python。我创建了一个函数来执行更新(即将尚未加载的QGIS中的文件或shapefile插入到它们各自的组中)。从本质上讲,这些步骤细分如下:

  • 步骤1
    • 用于“策略约束”目录:
      • 获取一个列表来遍历该目录,并检查组名是否与目录的名称相匹配。
        • 如果有匹配,从列表中删除它,否则;
        • 加载文件,根据它们的得分属性添加样式符号(如果没有现有的得分属性,则创建一个),并使其可见。

代码语言:javascript
复制
- Repeat for the "_Technical constraints_" directory.
  • 步骤2
    • 用于“策略约束”目录:
      • 获取一个列表来存储加载的shapefile的名称,并获取另一个列表来存储目录中所有shapefile的名称。
        • 创建一个包含未加载的shapefiles的最终列表,并加载这些文件,根据它们的分数attribute...etc添加一个样式符号。

代码语言:javascript
复制
- Repeat for the "_Technical constraints_" directory.
- Repeat for the "_Context_" directory but only load the shapefiles, no styling is applied, nor making it visible.
- Repeat for the "_Area of interest_" directory and load the shapefiles, load the style symbology from a file but do not make it visible.
  • 如果没有目录为空,则显示一条成功消息,否则显示警告消息。

代码包含许多重复的内容,例如存储、检查属性是否存在、添加一个新的属性以及多个os.walks()。使代码更加考虑的最佳方法是什么?

以下是代码:

代码语言:javascript
复制
# Define root and groups
root = QgsProject.instance().layerTreeRoot()
policy_group = root.findGroup('Policy')
technical_group = root.findGroup('Technical')
area_of_interest_group = root.findGroup('Area of interest')
context_group = root.findGroup('Context')
bridge = iface.layerTreeCanvasBridge()

# Use CRS of city shapefile
for fname in glob.glob(QgsProject.instance().readPath("./") + '/Processing scripts/Shapefile conversion/City/*.shp'):
    initial_crs = QgsVectorLayer( fname, '', 'ogr' ).crs().authid()
    get_crs = int(initial_crs.split(":",1)[-1].split()[0])

# Walk through folders and directories
pol_root, pol_dirs, pol_files = os.walk(QgsProject.instance().readPath("./") + '/Constraints/Policy constraints').next()
tech_root, tech_dirs, tech_files = os.walk(QgsProject.instance().readPath("./") + '/Constraints/Technical constraints').next()

# Set message depending on level
message_level = 1
#############################################################
# Step 1: Adding new groups of layers if group does not exist
#############################################################
# For Policy constraints
# Check if existing group name matches constraint folder name
for group in policy_group.children():
    # If it matches, remove from list
    if group.name() in pol_dirs:
        pol_dirs.remove(group.name())
    else:
        pass
# Check if list is empty
if not pol_dirs:
    pass
else:
    # Else add group containing folder name and its shapefiles
    for folder_name in pol_dirs:
        # Before adding group and its shapefiles, check if folder is empty
        # If folder contains shapefiles
        if glob.glob(pol_root + "/" + folder_name + "/*.shp"):
            group = policy_group.addGroup(folder_name)
            for shapefile in glob.glob(pol_root + "/" + folder_name + "/*.shp"):
                layer = QgsVectorLayer(shapefile, os.path.splitext(os.path.basename(shapefile))[0], "ogr" )
                idx = layer.fieldNameIndex("Score")   
                crs = layer.crs()
                crs.createFromId(get_crs)
                layer.setCrs(crs)
                QgsMapLayerRegistry.instance().addMapLayer(layer, False)
                group.insertChildNode(-1, QgsLayerTreeLayer(layer))                 
                # Set up layer symbology corresponding to saved score
                symbols = layer.rendererV2().symbols()
                symbol = symbols[0]  
                if idx != -1:                  
                    if layer.minimumValue(idx) == 1 and layer.maximumValue(idx) == 1:
                        symbol.setColor(QColor('#dbffdb'))
                    if layer.minimumValue(idx) == 2 and layer.maximumValue(idx) == 2:
                        symbol.setColor(QColor('#f0ab64'))
                    if layer.minimumValue(idx) == 3 and layer.maximumValue(idx) == 3:
                        symbol.setColor(QColor('#963634'))
                    if layer.minimumValue(idx) == 4 and layer.maximumValue(idx) == 4:
                        symbol.setColor(QColor('#1d1b10'))
                    elif layer.minimumValue(idx) != layer.maximumValue(idx):
                        style_rules = (
                            ('Possible', """"Score" IS NULL OR "Score" = 1""", '#dbffdb'),
                            ('Intermediate', """"Score" = 2""", '#f0ab64'),
                            ('Sensitive', """"Score" = 3""", '#963634'),
                            ('Showstopper', """"Score" = 4""", '#1d1b10'),
                        )
                        # Create a new rule-based renderer
                        symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
                        renderer = QgsRuleBasedRendererV2(symbol)
                        # Get the "root" rule
                        root_rule = renderer.rootRule()
                        for label, expression, color_name in style_rules:
                            # Set outline colour to match that of polygon fill colour
                            # create a clone (i.e. a copy) of the default rule
                            rule = root_rule.children()[0].clone()
                            # set the label, expression and color
                            rule.setLabel(label)
                            rule.setFilterExpression(expression)
                            rule.symbol().setColor(QColor(color_name))
                            # Append the rule to the list of rules
                            root_rule.appendChild(rule)
                        # Delete the default rule
                        root_rule.removeChildAt(0)
                        # Apply the renderer to the layer
                        layer.setRendererV2(renderer)
                        iface.legendInterface().setLayerExpanded(layer, False)
                    layer.triggerRepaint()
                    iface.legendInterface().refreshLayerSymbology(layer)
                    iface.legendInterface().setLayerVisible(layer, True)
                else:
                    layer.startEditing()
                    layer.dataProvider().addAttributes( [ QgsField("Score", QVariant.Int) ] )
                    layer.updateFields()
                    for feat in layer.getFeatures():
                        layer.changeAttributeValue(feat.id(), layer.fieldNameIndex('Score'), '1')
                    layer.commitChanges()
                    symbol.setColor(QColor('#dbffdb'))
                    iface.legendInterface().refreshLayerSymbology(layer)
                    iface.legendInterface().setLayerVisible(layer, True)
            message_level = 1
        else:
            # If folder is empty
            message_level = 2

# For Technical constraints
# Check if existing group name matches constraint folder name
for group in technical_group.children():
    # If it matches, remove from list
    if group.name() in tech_dirs:
        tech_dirs.remove(group.name())
    else:
        pass
# Check if list is empty
if not tech_dirs:
    pass
else:
    # Else add group containing folder name and its shapefiles
    for folder_name in tech_dirs:
        # Before adding group and its shapefiles, check if folder is empty
        # If folder contains shapefiles
        if glob.glob(tech_root + "/" + folder_name + "/*.shp"):
            group = technical_group.addGroup(folder_name)
            for shapefile in glob.glob(tech_root + "/" + folder_name + "/*.shp"):
                layer = QgsVectorLayer(shapefile, os.path.splitext(os.path.basename(shapefile))[0], "ogr" )
                idx = layer.fieldNameIndex("Score")                    
                crs = layer.crs()
                crs.createFromId(get_crs)
                layer.setCrs(crs)
                QgsMapLayerRegistry.instance().addMapLayer(layer, False)
                group.insertChildNode(-1, QgsLayerTreeLayer(layer))                        
                # Set up layer symbology corresponding to saved score
                symbols = layer.rendererV2().symbols()
                symbol = symbols[0]   
                if idx != -1:                 
                    if layer.minimumValue(idx) == 1 and layer.maximumValue(idx) == 1:
                        symbol.setColor(QColor('#dbffdb'))
                    if layer.minimumValue(idx) == 2 and layer.maximumValue(idx) == 2:
                        symbol.setColor(QColor('#f0ab64'))
                    if layer.minimumValue(idx) == 3 and layer.maximumValue(idx) == 3:
                        symbol.setColor(QColor('#963634'))
                    elif layer.minimumValue(idx) != layer.maximumValue(idx):
                        style_rules = (
                            ('Favourable', """"Score" IS NULL OR "Score" = 1""", '#dbffdb'),
                            ('Likely', """"Score" = 2""", '#f0ab64'),
                            ('Unlikely', """"Score" = 3""", '#963634'),
                        )
                        # Create a new rule-based renderer
                        symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
                        renderer = QgsRuleBasedRendererV2(symbol)
                        # Get the "root" rule
                        root_rule = renderer.rootRule()
                        for label, expression, color_name in style_rules:
                            # Set outline colour to match that of polygon fill colour
                            # Create a clone (i.e. a copy) of the default rule
                            rule = root_rule.children()[0].clone()
                            # Set the label, expression and color
                            rule.setLabel(label)
                            rule.setFilterExpression(expression)
                            rule.symbol().setColor(QColor(color_name))
                            # Append the rule to the list of rules
                            root_rule.appendChild(rule)
                        # Delete the default rule
                        root_rule.removeChildAt(0)
                        # Apply the renderer to the layer
                        layer.setRendererV2(renderer)
                        iface.legendInterface().setLayerExpanded(layer, False)
                    layer.triggerRepaint()
                    iface.legendInterface().refreshLayerSymbology(layer)
                    iface.legendInterface().setLayerVisible(layer, True)
                else:
                    layer.startEditing()
                    layer.dataProvider().addAttributes( [ QgsField("Score", QVariant.Int) ] )
                    layer.updateFields()
                    for feat in layer.getFeatures():
                        layer.changeAttributeValue(feat.id(), layer.fieldNameIndex('Score'), '1')
                    layer.commitChanges()
                    symbol.setColor(QColor('#dbffdb'))
                    iface.legendInterface().refreshLayerSymbology(layer)
                    iface.legendInterface().setLayerVisible(layer, True)
            if message_level == 2:
                message_level = 2
            else:
                message_level = 1
        else:
            # If folder is empty
            message_level = 2


###################################################################
# Step 2: Update existing groups with newly added layers to folders
###################################################################
# For Policy constraints
pol_root, pol_dirs, pol_files = os.walk(QgsProject.instance().readPath("./") + '/Constraints/Policy constraints').next()
# Create empty list
pol_list = []
# Find all layers loaded in QGIS
for group in policy_group.children():
    for pol_layer in group.children():
        pol_list.append(pol_layer.layer().source())
new_pol_list = [x.encode('UTF8') for x in pol_list]
second_new_pol_list = [l.replace('\\', '/') for l in new_pol_list]
# Create empty list
pol_dir_list = []
# Find all layers in constraint directories
for folder_name in pol_dirs:
    for shapefile in glob.glob(pol_root + "/" + folder_name + "/*.shp"):
        pol_dir_list.append(shapefile)
# Format the list to match same format as the list above
new_pol_dir_list = [l.replace('\\', '/') for l in pol_dir_list]
# Find paths of shapefiles in directories which are not loaded in QGIS
pol_missing_shapefiles = [x for x in new_pol_dir_list if x not in second_new_pol_list]
# For all shapefiles not loaded in QGIS, add them to relevant group with symbology
for shapefile in pol_missing_shapefiles:
    paths = os.path.dirname(shapefile)
    group_name = paths.rsplit('/', 1)[-1]
    group = root.findGroup(group_name)
    layer = QgsVectorLayer(shapefile, os.path.splitext(os.path.basename(shapefile))[0], "ogr" )
    idx = layer.fieldNameIndex("Score")   
    crs = layer.crs()
    crs.createFromId(get_crs)
    layer.setCrs(crs)
    QgsMapLayerRegistry.instance().addMapLayer(layer, False)
    group.insertChildNode(-1, QgsLayerTreeLayer(layer))
    # Set up layer symbology corresponding to saved score
    symbols = layer.rendererV2().symbols()
    symbol = symbols[0] 
    if idx != -1:            
        if layer.minimumValue(idx) == 1 and layer.maximumValue(idx) == 1:
            symbol.setColor(QColor('#dbffdb'))
        if layer.minimumValue(idx) == 2 and layer.maximumValue(idx) == 2:
            symbol.setColor(QColor('#f0ab64'))
        if layer.minimumValue(idx) == 3 and layer.maximumValue(idx) == 3:
            symbol.setColor(QColor('#963634'))
        if layer.minimumValue(idx) == 4 and layer.maximumValue(idx) == 4:
            symbol.setColor(QColor('#1d1b10'))
        elif layer.minimumValue(idx) != layer.maximumValue(idx):
            style_rules = (
                ('Possible', """"Score" IS NULL OR "Score" = 1""", '#dbffdb'),
                ('Intermediate', """"Score" = 2""", '#f0ab64'),
                ('Sensitive', """"Score" = 3""", '#963634'),
                ('Showstopper', """"Score" = 4""", '#1d1b10'),
            )
            # Create a new rule-based renderer
            symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
            renderer = QgsRuleBasedRendererV2(symbol)
            # Get the "root" rule
            root_rule = renderer.rootRule()
            for label, expression, color_name in style_rules:
                # Set outline colour to match that of polygon fill colour
                # Create a clone (i.e. a copy) of the default rule
                rule = root_rule.children()[0].clone()
                # Set the label, expression and color
                rule.setLabel(label)
                rule.setFilterExpression(expression)
                rule.symbol().setColor(QColor(color_name))
                # Append the rule to the list of rules
                root_rule.appendChild(rule)
            # Delete the default rule
            root_rule.removeChildAt(0)
            # Apply the renderer to the layer
            layer.setRendererV2(renderer)
            iface.legendInterface().setLayerExpanded(layer, False)
        layer.triggerRepaint()
        iface.legendInterface().refreshLayerSymbology(layer)
        iface.legendInterface().setLayerVisible(layer, True)
    else:
        layer.startEditing()
        layer.dataProvider().addAttributes( [ QgsField("Score", QVariant.Int) ] )
        layer.updateFields()
        for feat in layer.getFeatures():
            layer.changeAttributeValue(feat.id(), layer.fieldNameIndex('Score'), '1')
        layer.commitChanges()
        symbol.setColor(QColor('#dbffdb'))
        iface.legendInterface().refreshLayerSymbology(layer)
        iface.legendInterface().setLayerVisible(layer, True)
    if message_level == 2:
        message_level = 2
    if message_level == 1:
        message_level = 1


# For Technical constraints
tech_root, tech_dirs, tech_files = os.walk(QgsProject.instance().readPath("./") + '/Constraints/Technical constraints').next()
# Create empty list
tech_list = []
# Find all layers loaded in QGIS
for group in technical_group.children():
    for tech_layer in group.children():
        tech_list.append(tech_layer.layer().source())
new_tech_list = [x.encode('UTF8') for x in tech_list]
second_new_tech_list = [l.replace('\\', '/') for l in new_tech_list]
# Create empty list
tech_dir_list = []
# Find all layers in constraint directories
for folder_name in tech_dirs:
    for shapefile in glob.glob(tech_root + "/" + folder_name + "/*.shp"):
        tech_dir_list.append(shapefile)
# Format the list to match same format as the list above
new_tech_dir_list = [l.replace('\\', '/') for l in tech_dir_list]
# Find paths of shapefiles in directories which are not loaded in QGIS
tech_missing_shapefiles = [x for x in new_tech_dir_list if x not in second_new_tech_list]
# For all shapefiles not loaded in QGIS, add them to relevant group with symbology
for shapefile in tech_missing_shapefiles:
    paths = os.path.dirname(shapefile)
    group_name = paths.rsplit('/', 1)[-1]
    group = root.findGroup(group_name)
    layer = QgsVectorLayer(shapefile, os.path.splitext(os.path.basename(shapefile))[0], "ogr" )
    idx = layer.fieldNameIndex("Score")  
    crs = layer.crs()
    crs.createFromId(get_crs)
    layer.setCrs(crs)
    QgsMapLayerRegistry.instance().addMapLayer(layer, False)
    group.insertChildNode(-1, QgsLayerTreeLayer(layer))
    # Set up layer symbology corresponding to saved score
    symbols = layer.rendererV2().symbols()
    symbol = symbols[0]                 
    if idx != -1:                 
        if layer.minimumValue(idx) == 1 and layer.maximumValue(idx) == 1:
            symbol.setColor(QColor('#dbffdb'))
        if layer.minimumValue(idx) == 2 and layer.maximumValue(idx) == 2:
            symbol.setColor(QColor('#f0ab64'))
        if layer.minimumValue(idx) == 3 and layer.maximumValue(idx) == 3:
            symbol.setColor(QColor('#963634'))
        elif layer.minimumValue(idx) != layer.maximumValue(idx):
            style_rules = (
                ('Favourable', """"Score" IS NULL OR "Score" = 1""", '#dbffdb'),
                ('Likely', """"Score" = 2""", '#f0ab64'),
                ('Unlikely', """"Score" = 3""", '#963634'),
            )
            # Create a new rule-based renderer
            symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
            renderer = QgsRuleBasedRendererV2(symbol)
            # Get the "root" rule
            root_rule = renderer.rootRule()
            for label, expression, color_name in style_rules:
                # Set outline colour to match that of polygon fill colour
                # Create a clone (i.e. a copy) of the default rule
                rule = root_rule.children()[0].clone()
                # Set the label, expression and color
                rule.setLabel(label)
                rule.setFilterExpression(expression)
                rule.symbol().setColor(QColor(color_name))
                # Append the rule to the list of rules
                root_rule.appendChild(rule)
            # Delete the default rule
            root_rule.removeChildAt(0)
            # Apply the renderer to the layer
            layer.setRendererV2(renderer)
            iface.legendInterface().setLayerExpanded(layer, False)
        layer.triggerRepaint()
        iface.legendInterface().refreshLayerSymbology(layer)
        iface.legendInterface().setLayerVisible(layer, True)
    else:
        layer.startEditing()
        layer.dataProvider().addAttributes( [ QgsField("Score", QVariant.Int) ] )
        layer.updateFields()
        for feat in layer.getFeatures():
            layer.changeAttributeValue(feat.id(), layer.fieldNameIndex('Score'), '1')
        layer.commitChanges()
        symbol.setColor(QColor('#dbffdb'))
        iface.legendInterface().refreshLayerSymbology(layer)
        iface.legendInterface().setLayerVisible(layer, True)
    if message_level == 2:
        message_level = 2
    if message_level == 1:
        message_level = 1


# For Context layers
context_root, context_dirs, context_files = os.walk(QgsProject.instance().readPath("./") + '/Geospatial information/Context').next()
# Create empty list
context_list = []
# Find all layers loaded in QGIS
for context_layer in context_group.children():
    try:
        context_list.append(context_layer.layer().source())
    except AttributeError:
        pass
new_context_list = [x.encode('UTF8') for x in context_list]
second_new_context_list = [l.replace('\\', '/') for l in new_context_list]
# Create empty list
context_dir_list = []
# Find all layers in context directory
for context_file in glob.glob(context_root + "/*"):
    context_dir_list.append(context_file)
# Format the list to match same format as the list above
new_context_dir_list = [l.replace('\\', '/') for l in context_dir_list]
# Find paths of shapefiles in directories which are not loaded in QGIS
context_missing_files = [x for x in new_context_dir_list if x not in second_new_context_list]
# For all shapefiles not loaded in QGIS, add them to relevant group with symbology
for file in context_missing_files:
    if file.lower().endswith('.shp'):
        vlayer = QgsVectorLayer(file, os.path.splitext(os.path.basename(file))[0], "ogr" )
        crs.createFromId(get_crs)
        vlayer.setCrs(crs)
        QgsMapLayerRegistry.instance().addMapLayer(vlayer, False)
        context_group.insertChildNode(-1, QgsLayerTreeLayer(vlayer))
        iface.legendInterface().setLayerVisible(vlayer, False)
        order = bridge.customLayerOrder()
        order.insert( 0, order.pop(order.index(vlayer.id())))
        bridge.setCustomLayerOrder(order) 
    if file.lower().endswith('.tif'):
        fileName = file
        fileInfo = QFileInfo(fileName)
        baseName = fileInfo.baseName()
        crs.createFromId(get_crs)
        rlayer.setCrs(crs)
        QgsMapLayerRegistry.instance().addMapLayer(rlayer, False)
        rlayer = QgsRasterLayer(fileName, baseName)
        context_group.insertChildNode(-1, QgsLayerTreeLayer(rlayer))
        iface.legendInterface().setLayerVisible(rlayer, False)
        order = bridge.customLayerOrder()
        order.insert( 0, order.pop(order.index(rlayer.id())))
        bridge.setCustomLayerOrder(order) 


# For Scope layers
scope_root, scope_dirs, scope_files = os.walk(QgsProject.instance().readPath("./") + '/Geospatial information/Area of interest').next()
# Create empty list
scope_list = []
# Find all layers loaded in QGIS
for scope_layer in area_of_interest_group.children():
    scope_list.append(scope_layer.layer().source())
new_scope_list = [x.encode('UTF8') for x in scope_list]
second_new_scope_list = [l.replace('\\', '/') for l in new_scope_list]
# Create empty list
scope_dir_list = []
# Find all layers in scope directory
for shapefile in glob.glob(scope_root + "/*.shp"):
    scope_dir_list.append(shapefile)
# Format the list to match same format as the list above
new_scope_dir_list = [l.replace('\\', '/') for l in scope_dir_list]
# Find paths of shapefiles in directories which are not loaded in QGIS
scope_missing_shapefiles = [x for x in new_scope_dir_list if x not in second_new_scope_list]
# For all shapefiles not loaded in QGIS, add them to relevant group with symbology
for shapefile in scope_missing_shapefiles:
    layer = QgsVectorLayer(shapefile, os.path.splitext(os.path.basename(shapefile))[0], "ogr" )
    crs = layer.crs()
    crs.createFromId(get_crs)
    layer.setCrs(crs)
    QgsMapLayerRegistry.instance().addMapLayer(layer, False)
    area_of_interest_group.insertChildNode(-1, QgsLayerTreeLayer(layer))
    layer.loadNamedStyle(self.plugin_dir + '/styles/scope_style.qml')
    iface.legendInterface().refreshLayerSymbology(layer)
    iface.legendInterface().setLayerVisible(layer, False)
    if message_level == 2:
        message_level = 2
    if message_level == 1:
        message_level = 1

###################################################################  
# Show messages based on level        
#if message_level == 0: 
#    iface.messageBar().pushSuccess( u'Nothing to update', '' )
iface.messageBar().clearWidgets()
if message_level == 1:
    iface.messageBar().pushSuccess( u'Project updated', '' )
if message_level == 2:
    iface.messageBar().pushWarning( u'Project updated but some constraint(s) were not added due to \
                empty directory', '' )
EN

回答 1

Code Review用户

回答已采纳

发布于 2017-08-21 19:54:43

哇。这是很多代码。首先需要使用它的是模块化。您需要构造代码,以便命名函数和/或类封装共享功能,并使其更具可读性。例如,如果我正在编写一个函数,该函数接受一些用户输入,对其进行验证,并执行大量操作,那么我将得到非常难以理解的50+代码行。如果我把它放在函数里的话。

代码语言:javascript
复制
def foo_the_bar(bar):
    user_foo = None
    while not user_foo:
        user_foo = get_user_foo()

    return do_the_foo(user_foo, bar)

对于任何人(尤其是你)来说,当你回到它的时候,它比你更容易理解。

代码语言:javascript
复制
def foo_the_bar(bar):
    user_foo = None
    while not user_foo:
        user_foo = raw_input("What is your foo? ")
        if len(user_foo) < 10:
            print "That isn't a good foo"
            user_foo = None
        # many other validation conditions

    user_foo = initialize_foo(user_foo)
    # do other operations
    return boo.fooer(user_foo)

诚然,这是一个愚蠢的例子,但它使您的代码的逻辑结构更加简单。如果您不确定如何拆分代码,或者如果没有太多的重复在逻辑上分离,请查找您的注释。如果注释没有添加任何值,例如

代码语言:javascript
复制
# add two numbers
x = y + z

那就删掉它。同样,如果它解释了代码是如何工作的(除非非常复杂),那么也要删除它。如果它解释了代码所做的事情,那么将该代码移动到一个函数中,并为该函数使用一个很好的名称来解释它所做的事情。最后,如果它解释了为什么代码是这样做的,例如

代码语言:javascript
复制
# remove the invalid element that the 3rd-party library always appends
x = x[:-1]

您可以将注释保留在适当的位置(否则没有人会理解您为什么要做某件事情),或者您可以将它移动到一个函数中(如果您必须多次执行相同的操作,建议这样做)。

代码语言:javascript
复制
def remove_invalid_element_from_<mythirdpartylib>(arr):
    return arr[:-1]

在您的示例中,我不打算详细介绍整个~500行代码,但我将重点介绍几个要点。

可能用于模块化代码的地方.

上面,当你描述工作流程时,只要你使用“重复”这个词,你就可以把它放入一个函数中。例如,第一步在技术组和策略组处理之间几乎没有区别:

  1. 它们有不同的变量名。
  2. 使用的颜色不同
  3. 样式规则是不同的
  4. 消息级别是不同的。

通过将其移动到一个共享函数中,这些都非常容易解决。

  1. 您不需要函数中的特定变量名--而不是pol_dirstech_dirs,只需使用dirs,而对于technical_grouppolicy_group,则只需使用group
  2. 只需将颜色字典(见下文)作为参数传入,并将其适当设置为组类型。
  3. 与2相同,但样式规则元组除外。
  4. 不要跟踪函数内部的消息级别,只要返回消息级别应该是什么以及函数之外的消息级别,您就可以获得这两个级别的最大值。

这样做几乎不需要任何工作,可以节省近100行代码。对于步骤2,您可以做一些非常类似的事情--代码在各个部分之间更加不同,但是如果您在这方面很聪明,您可以删除大量的重复。

以下是一些其他的一般性建议。

不使用pass只是为了说“什么都不做”

通常情况下,您可以完全排除pass,或者稍微重构代码以避免它。在99.9%的案例中,它的价值微乎其微。

代码语言:javascript
复制
for group in policy_group.children():
    # If it matches, remove from list
    if group.name() in pol_dirs:
        pol_dirs.remove(group.name())
    else:
        pass

因为您不只是在else分支中做任何事情,所以只需删除它。

代码语言:javascript
复制
if not pol_dirs:
    pass
else:
    # Else add group containing folder name and its shapefiles
    for folder_name in pol_dirs:

在这种情况下,只需倒转你的情况。

代码语言:javascript
复制
if pol_dirs:
    for folder_name in pol_dirs:

但更简单的是,因为pol_dirs是一个列表,所以您可以只做

代码语言:javascript
复制
for folder_name in pol_dirs:

使用for-loop迭代一个空的迭代,只需跳过循环。

安全地连接到您的文件路径

您正在手动构建您的文件路径。

代码语言:javascript
复制
if glob.glob(pol_root + "/" + folder_name + "/*.shp"):
代码语言:javascript
复制
for shapefile in glob.glob(pol_root + "/" + folder_name + "/*.shp"):
代码语言:javascript
复制
os.walk(QgsProject.instance().readPath("./") + '/Constraints/Policy constraints').next()

首先,原始字符串连接往往效率很低。其次,文件分隔符并不保证对所有操作系统都是相同的。虽然您现在可能不打算使用其他操作系统,但是如果您想要这样做,为什么要更加困难呢?此外,通过使用一个常见的成语,任何其他Python程序员都会很容易地识别代码行应该做什么。只需使用os.path.join即可。这些线会变成

代码语言:javascript
复制
if glob.glob(os.path.join(pol_root, folder_name, "*.shp")):
代码语言:javascript
复制
for shapefile in glob.glob(os.path.join(pol_root, folder_name, "*.shp")):
代码语言:javascript
复制
os.walk(os.path.join(QgsProject.instance().readPath("./"), "Constraints", "Policy constraints")).next()

当你在做的时候,

将值放入变量中,如果您不止一次使用它们,

在这种情况下,您至少要做两次glob.glob(pol_root + "/" + folder_name + "/*.shp"),我相信还有其他的例子。为它找出一个很好的变量名,然后使用这个值。

为某些if

使用字典

代码语言:javascript
复制
if layer.minimumValue(idx) == 1 and layer.maximumValue(idx) == 1:
    symbol.setColor(QColor('#dbffdb'))
if layer.minimumValue(idx) == 2 and layer.maximumValue(idx) == 2:
    symbol.setColor(QColor('#f0ab64'))
if layer.minimumValue(idx) == 3 and layer.maximumValue(idx) == 3:
    symbol.setColor(QColor('#963634'))
if layer.minimumValue(idx) == 4 and layer.maximumValue(idx) == 4:
    symbol.setColor(QColor('#1d1b10'))

如果在字典中对值进行编码,这将更加简洁,如下所示:

代码语言:javascript
复制
color_mapping = { 1: "dbffdb", 2: "f0ab64", 3: "963634", 4: "1d1b10" }
if layer.minimumValue(idx) == layer.maximumValue(idx):
    color = color_mapping.get(layer.minimumValue(idx), "default color")
    symbol.setColor(QColor('#{color}'.format(color=color)))

将共享代码移出尽可能多的层,

当您在步骤1中处理策略文件夹时,您的条件如下

代码语言:javascript
复制
if idx != -1:
    ...
    iface.legendInterface().refreshLayerSymbology(layer)
    iface.legendInterface().setLayerVisible(layer, True)
else:
    ...
    iface.legendInterface().refreshLayerSymbology(layer)
    iface.legendInterface().setLayerVisible(layer, True)

只要将重复的代码移出if/else语句就可以了。

票数 6
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/173521

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档