如何将xml文件拖放到Qtableview中同时解析xml

How to drag a xml file and drop it in Qtableview and parse the xml at the same time

我想在QtableView或QtableWidget中拖拽一个XML文件,同时解析文件,在QtableView的不同列中显示。

我已经看过很多示例,但不确定从哪里开始,是否有任何教程或示例,我需要在 PyQt5 中完成。

我是第一次这样做,不确定如何开始。

我有一个非常简单的 XML 文件示例:每个图层名称是第一列,然后子标签是相同的相应属性。

<xtech>
  <Layer id="0" name="EM_UPKG">
    <SourceLayer>NoSource</SourceLayer>
    <Color>
      <R>255</R>
      <G>255</G>
      <B>0</B>
    </Color>
    <Offset>
      <value>114.215</value>
      <dependent>EM_AlN</dependent>
      <placement>Top</placement>
    </Offset>
    <Thickness>
      <value>50</value>
      <dependent>-1</dependent>
    </Thickness>
    <Material>air</Material>
    <Port>NO</Port>
    <Resistivity>0</Resistivity>
    <Transparency>0.8</Transparency>
    <Sheet_Priority>0</Sheet_Priority>
  </Layer>
  <Layer id="1" name="EM_UP">
    <SourceLayer>NoSource</SourceLayer>
    <Color>
      <R>255</R>
      <G>128</G>
      <B>0</B>
    </Color>
    <Offset>
      <value>164.215</value>
      <dependent>EM_UVIA</dependent>
      <placement>Top</placement>
    </Offset>
    <Thickness>
      <value>40</value>
      <dependent>-1</dependent>
    </Thickness>
    <Material>copper</Material>
    <Port>NO</Port>
    <Resistivity>0</Resistivity>
    <Transparency>0</Transparency>
    <Sheet_Priority>0</Sheet_Priority>
  </Layer>
  <Layer id="2" name="EM_UVIA">
    <SourceLayer>NoSource</SourceLayer>
    <Color>
      <R>128</R>
      <G>128</G>
      <B>0</B>
    </Color>
    <Offset>
      <value>114.215</value>
      <dependent>EM_AlN</dependent>
      <placement>Top</placement>
    </Offset>
    <Thickness>
      <value>50</value>
      <dependent>-1</dependent>
    </Thickness>
    <Material>copper</Material>
    <Port>NO</Port>
    <Resistivity>0</Resistivity>
    <Transparency>0</Transparency>
    <Sheet_Priority>0</Sheet_Priority>
  </Layer>
  <Layer id="3" name="EM_PKG">
    <SourceLayer>NoSource</SourceLayer>
    <Color>
      <R>255</R>
      <G>255</G>
      <B>0</B>
    </Color>
    <Offset>
      <value>114.215</value>
      <dependent>EM_AlN</dependent>
      <placement>Top</placement>
    </Offset>
    <Thickness>
      <value>20</value>
      <dependent>-1</dependent>
    </Thickness>
    <Material>air</Material>
    <Port>NO</Port>
    <Resistivity>0</Resistivity>
    <Transparency>0.8</Transparency>
    <Sheet_Priority>0</Sheet_Priority>
  </Layer>
  <Layer id="4" name="EM_PL">
    <SourceLayer>NoSource</SourceLayer>
    <Color>
      <R>255</R>
      <G>128</G>
      <B>0</B>
    </Color>
    <Offset>
      <value>114.215</value>
      <dependent>EM_AlN</dependent>
      <placement>Top</placement>
    </Offset>
    <Thickness>
      <value>95</value>
      <dependent>-1</dependent>
    </Thickness>
    <Material>copper</Material>
    <Port>NO</Port>
    <Resistivity>0</Resistivity>
    <Transparency>0</Transparency>
    <Sheet_Priority>0</Sheet_Priority>
  </Layer>
  <Mesh_Operations>
    <Model_Resolution>60</Model_Resolution>
    <Surface_Approximation>0.5</Surface_Approximation>
  </Mesh_Operations>
  <Port_Processing>
    <Use_Delta>True</Use_Delta>
    <hport_delta>5</hport_delta>
  </Port_Processing>
</xtech>

列应显示为:

目标层、颜色、偏移、厚度、MAterial、端口、Sheet电阻率、透明度、优先级和源层

所以我写了下面的代码来实现拖放,现在我必须研究如何解析 XML 文件。

    def __init__(self,parent=None):
    super().__init__()
    self.ui = Ui_MainWindowEtechEditor()
    self.ui.setupUi(self)
    self.setAcceptDrops(True)

    self.ui.pushButton.clicked.connect(self.opecolorEditor)

def opecolorEditor(self):
    color = QColorDialog(self)
    color.setSizeGripEnabled(True)
    color.layout().setSizeConstraint(QLayout.SetNoConstraint)
    color.show()


def dragEnterEvent(self, e):
    if e.mimeData().hasUrls:
        e.accept()
    else:
        e.ignore()

def dragMoveEvent(self, e):
    if e.mimeData().hasUrls:
        e.accept()
    else:
        e.ignore()

def dropEvent(self, e, event=None):
    if e.mimeData().hasUrls():
        e.setDropAction(QtCore.Qt.CopyAction)
        e.accept()

        drop_list = []
        for url in e.mimeData().urls():
            fName = (str(url.toLocalFile()))
            print("path ", fName)

        self.loadExtechfile(fName)

    else:
        e.ignore()

Qt 提供 QXmlStreamReader,一个 Xml 解析器。
解析器一次读取一个元素,保持当前元素类型和内容,所以唯一的问题是了解如何跟踪嵌套元素,只要源格式正确。

readNext() returns xml 元素的元素类型 ("token")(也可以通过 tokenType() 访问)。
最重要的标记类型是 StartElementEndElement,您可以使用 QXmlStreamReader.attributes().value(attrName) 来读取 StartElement 的属性,并使用 xml.readElementText() 来读取它的内容,这可能是它的文本内容甚至是子元素的原始文本,只要 QXmlStreamReader.IncludeChildElements 作为最后一个参数提供。

Columns = ('Target Layer', 'Color', 'Offset', 'Thickness', 'Material', 
    'Port', 'Resistivity', 'Transparency', 'Sheet_Priority', 'SourceLayer')
SimpleFields = ('SourceLayer', 'Material', 'Resistivity', 'Port', 
    'Transparency', 'Sheet_Priority')

class Table(QtWidgets.QTableView):
    def __init__(self, *args, **kwargs):
        QtWidgets.QTableView.__init__(self, *args, **kwargs)
        self.setAcceptDrops(True)
        model = QtGui.QStandardItemModel(0, len(Columns))
        model.setHorizontalHeaderLabels(Columns)
        self.setModel(model)

    def dragEnterEvent(self, e):
        # be careful, as you forgot the parenthesis of hasUrls()!
        if e.mimeData().hasUrls() and any(u.isLocalFile() for u in e.mimeData().urls()):
            e.accept()
        else:
            e.ignore()

# ...

    def dropEvent(self, e):
        f = QtCore.QFile(e.mimeData().urls()[0].toLocalFile())
        if not f.open(f.ReadOnly):
            return
        xml = QtCore.QXmlStreamReader(f)
        valid = False
        currentLayer = None
        while not xml.atEnd():
            if xml.readNext() == xml.StartElement:
                # track the first start element and ensure that it's of
                # the right type, otherwise ignore the file
                if not valid:
                    if xml.name() != 'xtech':
                        break
                    valid = True
                    continue
                if xml.name() == 'Layer':
                    # if a currentLayer exists, we assume it's finished,
                    # so we can process it and add its items
                    if currentLayer:
                        self.addLayer(currentLayer)
                    currentLayer = {'Target Layer': xml.attributes().value('name')}
                elif xml.name() in SimpleFields:
                    currentLayer[xml.name()] = xml.readElementText()
                elif xml.name() == 'Color':
                    currentLayer['Color'] = color = QtGui.QColor()
                    # this is a "nested" field, read its contents until
                    # it's reached the end element;
                    while xml.readNext():
                        if xml.tokenType() == xml.EndElement and xml.name() == 'Color':
                            break
                        elif xml.name() == 'R':
                            color.setRed(int(xml.readElementText()))
                        elif xml.name() == 'G':
                            color.setGreen(int(xml.readElementText()))
                        elif xml.name() == 'B':
                            color.setBlue(int(xml.readElementText()))
                # use the same logic for all other nested fields.
        f.close()

    def addLayer(self, layer):
        # create an empty "row" that can be filled with items in the correct
        # order, and that will leave empty cells if some fields do not exist
        row = [None] * len(Columns)
        for field, value in layer.items():
            item = QtGui.QStandardItem()
            if isinstance(value, QtGui.QColor):
                item.setBackground(value)
            else:
                item.setData(value, QtCore.Qt.DisplayRole)
            row[Columns.index(field)] = item
        self.model().appendRow(row)

在解析元素时需要注意字母的大小写,您可能应该使用 xml.name().lower() 来与字段名称进行比较。

经过几个小时的努力,我找到了另一个解析器 xml.etree.ElementTree 并实现了如下所示的解析器代码,它非常有用。

    def loadExtechfile(self,fName):
    print("load file from  ", fName)
    tree = ET.parse(fName)
    root = tree.getroot()

    for layer in root.findall('Layer'):
        layerName =layer.get('name')
        sourceLayer = layer.find('SourceLayer').text

        for color in layer.findall('Color'):
            r = color.find('R').text
            g = color.find('G').text
            b = color.find('B').text

        combinedRGB = r+","+g+","+b

        for offset in layer.findall('Offset'):
            value = offset.find('value').text
            dep = offset.find('dependent').text
            plc = offset.find('placement').text

        if dep == "-1" and plc == "None":
            combinedOffset = value
        else:
            combinedOffset = value + "(= " + dep + " [" + plc + "])"

        for thickness in layer.findall('Thickness'):
            valueT = thickness.find('value').text
            depT = thickness.find('dependent').text

        if depT != "-1":
            combinedThickness = valueT + "(=" + depT + ")"
        else:
            combinedThickness = valueT
        material = layer.find('Material').text
        port = layer.find('Port').text
        res = layer.find('Resistivity').text
        trans = layer.find('Transparency').text
        sheetPrio = layer.find('Sheet_Priority').text
        #print(layerName,r,g,b,sourceLayer,material,port,res,trans,sheetPrio)