简体   繁体   中英

python, ElementTree, Issue when adding and removing subelements from xml file

I'm getting an issue when removing and adding elements to a xml file via ElementTree and Tkinter GUI.

This is just a simplified part of a piece of code that's why my naming conventions may seem a little weird and I'm also fairly new to python (I'm in year 12), nevertheless:

The issue is when a box(subelement of room0) is added to the xml file after removing some other boxes it adds all the boxes that were previously removed from the xml file, instead of just one.

my goal is to:

  • add boxes to the xml file under the room 0 element when the add box button is pressed

  • remove the respective box to its minus button

  • then be able to add 1 box per time the add box button is pressed (instead all that were removed) after subtracting boxes

XML tree: (file called "summary.xml"

<data>
    <room0 />
</data>

Code:

import tkinter as tk
from tkinter import *
import xml.etree.ElementTree as ET

root = Tk()
#root dimentions
root.geometry("500x560")
root.title("Box & room organiser")
root.resizable(False,False)
root.configure(background = "#2a2828")
#xml
tree = ET.parse("summary.xml")
xmlroot = tree.getroot()

#function that indents and writes xml file when called
def write_xml(tree):
    ET.indent(tree, space = '\t', level = 0)
    tree.write('summary.xml')

#iterates through the root finding elements of xmlroot
for room0 in xmlroot:
    if room0.tag == 'room0':
        xml_room0 = room0

#counter variable for amount of boxes in xml file        
box_count = 0

def add_box():
    global box_count
    box_count = box_count + 1

    #if box count is 1 add box 1
    if box_count == 1:

        # adding room0's box 1 to the xml file
        for room0 in xmlroot.findall('room0'):
            box1=ET.Element('box1')
            room0.append(box1)

        #alternative way of adding-->
        # xml_room0_box1 = ET.SubElement(xml_room0, "box1")

        write_xml(tree)
        print('box1 added')

    elif box_count == 2:
        #adding room0's box 2 to the xml file
        for room0 in xmlroot.findall('room0'):
            box2=ET.Element('box2')
            room0.append(box2)
        # xml_room0_box2 = ET.SubElement(xml_room0, "box2")
        write_xml(tree)
        print('box2 added')

    elif box_count == 3:
        #adding room0's box 3 to the xml file
        for room0 in xmlroot.findall('room0'):
            box3=ET.Element('box3')
            room0.append(box3)
        # xml_room0_box3 = ET.SubElement(xml_room0, "box3")

        write_xml(tree)
        print('box3 added')

    elif box_count == 4:
        #adding room0's box 4 to the xml file
        for room0 in xmlroot.findall('room0'):
            box4=ET.Element('box4')
            room0.append(box4)
        # xml_room0_box4 = ET.SubElement(xml_room0, "box4")

        write_xml(tree)
        print('box4 added')

    elif box_count == 5:
        #adding room0's box 5 to the xml file
        for room0 in xmlroot.findall('room0'):
            box5=ET.Element('box5')
            room0.append(box5)
        # xml_room0_box5 = ET.SubElement(xml_room0, "box5")

        write_xml(tree)
        print('box5 added')

    elif box_count == 6:
        #adding room0's box 6 to the xml file
        for room0 in xmlroot.findall('room0'):
            box6=ET.Element('box6')
            room0.append(box6)
        # xml_room0_box6 = ET.SubElement(xml_room0, "box6")
        write_xml(tree)
        print('box6 added')

add_box_button = Button(root, text = "+", command = add_box)
add_box_button.pack()

        #alternative minus code using etree
        # from lxml import etree (add at top if using alternative)
        # for elem in parse_tree.findall('//box1'): #findall or xpath (no difference)
        #     parent = elem.getparent()
        #     parent.remove(elem)
        # print(etree.tostring(parse_tree))
        # parse_tree.write('summary.xml')
    
def minus_box1():
#removes box1 element from xml file
    global box_count
    box_count = box_count - 1

    tree =ET.parse('summary.xml')
    xml_root = tree.getroot()

    for room0 in xml_root:
        if room0.tag == 'room0':
            for box1 in room0:
                if box1.tag == 'box1':
                    room0.remove(box1)
    write_xml(tree)
    print('box1 minused')
    
def minus_box2():
    global box_count
    box_count = box_count - 1
    #removes box1 element from summary.xml
    tree=ET.parse('summary.xml')
    xmlroot = tree.getroot()

    for room0 in xmlroot:
        if room0.tag == 'room0':
            for box2 in room0:
                if box2.tag == 'box2':
                    room0.remove(box2)
    write_xml(tree)
    print('box2 minused')

def minus_box3():
    global box_count
    box_count = box_count - 1
    #removes box1 element from summary.xml
    tree=ET.parse('summary.xml')
    xmlroot = tree.getroot()

    for room0 in xmlroot:
        if room0.tag == 'room0':
            for box3 in room0:
                if box3.tag == 'box3':
                    room0.remove(box3)
    write_xml(tree)
    print('box3 minused')

def minus_box4():
    global box_count
    box_count = box_count - 1
    #removes box1 element from summary.xml
    tree=ET.parse('summary.xml')
    xmlroot = tree.getroot()

    for room0 in xmlroot:
        if room0.tag == 'room0':
            for box4 in room0:
                if box4.tag == 'box4':
                    room0.remove(box4)
    write_xml(tree)
    print('box4 minused')

def minus_box5():
    global box_count
    box_count = box_count - 1
    #removes box1 element from summary.xml
    tree=ET.parse('summary.xml')
    xmlroot = tree.getroot()

    for room0 in xmlroot:
        if room0.tag == 'room0':
            for box5 in room0:
                if box5.tag == 'box5':
                    room0.remove(box5)
    write_xml(tree)
    print('box5 minused')

def minus_box6():
    global box_count
    box_count = box_count - 1
    #removes box1 element from summary.xml
    tree=ET.parse('summary.xml')
    xmlroot = tree.getroot()

    for room0 in xmlroot:
        if room0.tag == 'room0':
            for box6 in room0:
                if box6.tag == 'box6':
                    room0.remove(box6)
    write_xml(tree)
    print('box6 minused')

#minus box buttons
room0_box1_minus_button = Button(root, text="-b1", bg = "#2b2828", command = minus_box1)
room0_box1_minus_button.pack()

room0_box2_minus_button = Button(root, text="-b2", bg = "#2b2828", command = minus_box2)
room0_box2_minus_button.pack()

room0_box3_minus_button = Button(root, text="-b3", bg = "#2b2828", command = minus_box3)
room0_box3_minus_button.pack()

room0_box4_minus_button = Button(root, text="-b4", bg = "#2b2828", command = minus_box4)
room0_box4_minus_button.pack()

room0_box5_minus_button = Button(root, text="-b5", bg = "#2b2828", command = minus_box5)
room0_box5_minus_button.pack()

room0_box6_minus_button = Button(root, text="-b6", bg = "#2b2828", command = minus_box6)
room0_box6_minus_button.pack()
root.mainloop()

The first thing I'd do is get rid of all of those if statements, weather they're causing the problem or not, they aren't very efficient.

I'd say replace them with a function that will get all of the 'boxes' from a 'room' in form of a list. That way you can just call len() (which gets the length of a list) and give you the number of all boxes in said room.

A simple function, something like this

def get_boxes(room):

tree = ET.parse("filename.xml")
root = tree.getroot()
boxList = []

for rooms in root:
    if rooms.tag == room:
        for boxes in rooms:
            boxList.append(boxes.attrib['name'])

return boxList

You can also get rid of all of those global statements, whenever you want to know the box count you can just call len(get_boxes(roomName)) .

Similarly, you don't need a function for every different box you remove, aslong as you can get which box the user is selecting a simple remove function will do, not unlike:

def remove_box(boxName, boxRoom):
tree = ET.parse("filename.xml")
root = tree.getroot()

for rooms in root:
    if rooms.tag == boxRoom:
        for boxes in rooms:
            if boxes.attrib['name'] == boxName:
                rooms.remove(boxes)
                break
            
ET.indent(tree, space = '\t', level=0)
tree.write("filename.xml", "utf-8")

It's the same story for adding a box, you can almost completely reuse a function to add boxes. Pass in the boxes name, which room it will be in, and the function can do the rest

def create_box(boxName, boxRoom):
tree = ET.parse("filename.xml")
root = tree.getroot()

for rooms in root:
    if rooms.tag == boxRoom:
        for boxes in rooms:
            ET.SubElement(boxes, 'box', name = boxName)
            
ET.indent(tree, space = '\t', level=0)
tree.write("filename.xml", "utf-8")

Some other suggestions, I would give the rooms there own names,

ET.SubElement("parent", 'room', name = "Kitchen")

and you can find that name with room.attrib['name'], you could do the same with boxes as well. That way the only two things you really need to collect from the user is what box they have selected and what room they are in.

I'm afraid I can't help you with the TKinter side of things, I always preferred PySimpleGUI, but if you can add and remove boxes, then just adding them into the add and remove functions could be enough.

My code is probably splattered with syntax errors, but I hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM