matplotlib: график рассеяния не обновляется при удалении данных из набора данных - PullRequest
0 голосов
/ 04 декабря 2018

У меня есть графический интерфейс, который позволяет пользователю создавать наборы точек, используя фигуры (например, круг с центром в X, Y и диаметром D, с N точками, где X, Y, D и N являются входными данными, используя поля ввода).

Из сгенерированного набора точек я делаю запись в виде дерева, а также строю точки.Затем пользователь может выбрать запись в виде дерева, чтобы выделить эти точки на графике.

Данные отображаются хорошо (я инициализирую график рассеяния с пустым набором данных, а затем добавляю данные к нему каждый раз, когда пользователь генерирует входные данные).У меня проблема, если пользователь удаляет данные (используя кнопку удаления в графическом интерфейсе).Словарь, в котором хранятся данные (self.patterns), корректно обновляется, но по какой-то причине данные не удаляются с графика.

Я предполагал очистить набор данных с помощью

self.scat = self.ptrnFig.scatter([], [], c="b", marker="o", s=40)

в функции plotAllPtrns () дал бы мне пустой разброс (точно так же, как при инициализации графика), и я мог бы просто повторно добавить оставшиеся точки в наборе данных в разброс, но это, похоже, не работает.

Я также попытался добавить строку

self.scat.remove()

в начале plotAllPtrns (), но тогда строится только последний набор пользовательских точек, а не все оставшиеся вТаблица.

import tkinter as tk
from tkinter import ttk
import numpy as np

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
from matplotlib.figure import Figure
matplotlib.use("TkAgg")


class MainGUI(tk.Tk):

    def __init__(self):
        tk.Tk.__init__(self)
        self.title('Title')
        self.geometry('750x500')

        for i in range(9):
            self.rowconfigure(i, weight=1)
            self.columnconfigure(i, weight=1)

        self.mydict = {}

        self.makeTable()
        self.initializePlot()
        self.makeWidgets()

    def makeWidgets(self):
        self.Labels = []
        self.Entries = []
        self.labText = [('X Location:'), ('Y Location:')]

        self.xGrid = 1
        self.yGrid = int(np.ceil(len(self.labText) / self.xGrid))
        i = 0
        for j in range(0, self.xGrid + 1, 2):
            for k in range(self.yGrid):
                if(i == len(self.labText)):
                    break
                else:
                    self.label = tk.Label(self, text=self.labText[i])
                    self.label.grid(column=j, row=k + 8, sticky='SW')
                    self.Labels.append(self.label)
                    self.entry = tk.Entry(self)
                    self.entry.insert(0, '0.0000')
                    self.entry.grid(column=j + 1, row=k + 8, sticky='NS')
                    self.Entries.append(self.entry)
                    i += 1

        self.addBtn = tk.Button(self, text='Add Entry', command=self.addEntry)
        self.addBtn.grid(column=self.xGrid + 1, row=self.yGrid + 9, sticky='NSEW')
        self.delBtn = tk.Button(self, text='Delete Entry', command=self.delEntry)
        self.delBtn.grid(column=self.xGrid, row=self.yGrid + 9, sticky='NSEW')

    def makeTable(self):
        tab_header = ['Pattern #', 'Description']
        self.tree = ttk.Treeview(self, columns=tab_header, height=5, show="headings")
        self.tree.grid(column=0, row=0, columnspan=2, rowspan=5, sticky='NSEW')
        self.tree.heading(tab_header[0], text=tab_header[0].title())
        self.tree.heading(tab_header[1], text=tab_header[1].title())
        self.tree.insert('', 'end', values=("", "(new)"))
        self.tree.bind("<ButtonRelease-1>", self.getPtrnTable)
        self.counter = 1

    def getPtrnTable(self, event):
        self.item = self.tree.identify_row(event.y)
        try:
            self.patNum = self.tree.item(self.item, 'values')[0]
        except IndexError:
            self.patNum = ''
        self.patternPlot()

    def addEntry(self):
        check = (len(self.tree.get_children()) == 1)
        self.description = "Location: " + self.Entries[0].get() + ", " + self.Entries[1].get()
        self.tree.insert('', 'end', values=(self.counter, self.description))
        newEntry = [float(self.Entries[0].get()), float(self.Entries[1].get())]
        patKey = 'pattern_' + str(self.counter - 1)
        self.mydict[patKey] = newEntry
        if(check == True):
            self.points = np.array(newEntry, ndmin=2)
        else:
            self.points = np.vstack((self.points, newEntry))
        self.counter += 1
        self.scat.set_offsets(self.points)
        self.patNum = ""
        print("mydict: ", self.mydict)
        self.patternPlot()

    def delEntry(self):
        treeCnt = len(self.tree.get_children())
        patKey = 'pattern_' + str(int(self.patNum) - 1)
        del self.mydict[patKey]
        items = self.tree.get_children()
        for i in range(int(self.patNum), treeCnt - 1):
            old_key = 'pattern_' + str(i)
            new_key = 'pattern_' + str(i - 1)
            self.mydict[new_key] = self.mydict.pop(old_key)
            self.tree.set(items[i], 'Description', self.tree.item(items[i + 1], 'values')[1])
        self.tree.delete(items[-1])
        self.patNum = ""
        self.patternPlot()
        print("mydict: ", self.mydict)

    def initializePlot(self):
        self.fig = Figure(figsize=(1, 1), dpi=100)
        self.ptrnFig = self.fig.add_subplot(111)
        self.ptrnFig.axis([-5, 5, -5, 5])
        self.ptrnFig.spines['left'].set_position('zero')
        self.ptrnFig.spines['bottom'].set_position('zero')
        self.ptrnFig.grid(True)
        self.canvas = FigureCanvasTkAgg(self.fig, self)
        self.canvas.draw()
        self.canvas.get_tk_widget().grid(column=2, row=0, columnspan=6, rowspan=5, sticky='NSEW')
        self.scat = self.ptrnFig.scatter([], [], c="b", marker="o")
        self.scat2 = self.ptrnFig.scatter([], [], c="#ff5733", marker="o")  # Orange Color

    def plotAllPtrns(self):
        self.scat = self.ptrnFig.scatter([], [], c="b", marker="o", s=40)
        self.scat2.remove()
        self.scat2 = self.ptrnFig.scatter([], [], c="#ff5733", marker="o")  # Orange Color

        for k, v in self.mydict.items():
            x = self.mydict[k][0]
            y = self.mydict[k][1]
            bolt = np.array([x, y], ndmin=2)
            self.scat.set_offsets(bolt)

        self.canvas.draw_idle()

    def patternPlot(self):
        # All data deleted - reinitialize plot
        if(len(self.tree.get_children()) == 1):
            plt.close(self.fig)
            self.initializePtrnPlot()
        # No specific entry selected - plot everything
        elif(self.patNum == ""):
            self.plotAllPtrns()
        # Specific entry chosen - highlight selected entry
        else:
            self.scat2.remove()
            self.scat2 = self.ptrnFig.scatter([], [], c="#ff5733", marker="o")  # Orange Color

            patKey = 'pattern_' + str(int(self.patNum) - 1)
            x = self.mydict[patKey][0]
            y = self.mydict[patKey][1]
            bolt = np.array([x, y], ndmin=2)

            self.scat2.set_offsets(bolt)
            self.canvas.draw_idle()


def main():
    MainGUI().mainloop()


if __name__ == '__main__':
    main()

Что мне здесь не хватает?Я чувствую, что очистка разброса и повторное заполнение (точно так же, как когда я просто добавляю данные) должны работать после удаления набора данных.

ОБНОВЛЕНИЕ: По запросу я добавил минимальную рабочую версию кода.Вместо того, чтобы создавать шаблоны, как описано выше, это просто отдельные точки.Добавьте точки по желанию, щелкните их в таблице, чтобы выделить их на графике, и используйте команду «Удалить», чтобы удалить их как из таблицы, так и из словаря, в котором они хранятся. График не обновляется при удалении точки, и я могуНе понимаю, почему.

Спасибо.

Ответы [ 2 ]

0 голосов
/ 05 декабря 2018

Как отметили @Treizh и @ImportanceOfBeingErnes, у меня действительно была слабая ссылка на self.patNum.Это не вызывало проблемы, но я обновил полную версию кода, чтобы улучшить это.Мне также не хватало вызова функции patternPlot () внутри функции delEntry (), поэтому она была исправлена ​​(хотя это не решило проблему с графиком).

Проблема, на мой взгляд, заключается в том, какset_offsets () работает, когда используется в цикле.Исходный код просматривает каждую запись в словаре и пытается добавить ее в набор данных, используемый для построения графиков.Казалось, что-то, что я не могу объяснить, происходило, что позволило данным, удаленным из набора данных, остаться (возможно, слабая ссылка на данные, хранящиеся в разбросе?).Чтобы это исправить, я сложил все записи в словаре (поскольку они всегда являются массивами Nx2), а затем использовал set_offsets (), чтобы сразу добавить весь набор данных в разброс.Исправлены функции delEntry () и обновлены функции plotAllPtrns ():

def delEntry(self):
    treeCnt = len(self.tree.get_children())
    patKey = 'pattern_' + str(int(self.patNum) - 1)
    del self.mydict[patKey]
    items = self.tree.get_children()
    for i in range(int(self.patNum), treeCnt - 1):
        old_key = 'pattern_' + str(i)
        new_key = 'pattern_' + str(i - 1)
        self.mydict[new_key] = self.mydict.pop(old_key)
        self.tree.set(items[i], 'Description', self.tree.item(items[i + 1], 'values')[1])
    self.tree.delete(items[-1])
    self.patNum = ""
    self.patternPlot()
    print("mydict: ", self.mydict)

def plotAllPtrns(self):
    self.scat.remove()
    self.scat = self.ptrnFig.scatter([], [], c="b", marker="o", s=40)
    self.scat2.remove()
    self.scat2 = self.ptrnFig.scatter([], [], c="#ff5733", marker="o")  # Orange Color

    patPlotData = np.vstack(([v for v in self.mydict.values()]))
    x = patPlotData[:, 0]
    y = patPlotData[:, 1]
    bolt = np.array([x, y], ndmin=2).T
    self.scat.set_offsets(bolt)

    self.canvas.draw_idle()

Я уверен, что я не совсем понимаю, почему старые данные остались в разбросе, даже если они были очищены (используя scatter ([], [])) или почему добавление всех данных одновременно с помощью set_offsets (), по-видимому, решает проблему, но это работает.

Если кто-то может объяснить, я бы хотел понять это лучше.Спасибо.

0 голосов
/ 04 декабря 2018

Я просто предоставлю следующий код как есть, который, кажется, решает проблему из вопроса.Внимание, хотя ... в некоторых случаях это приведет к другим ошибкам из-за того, что self.patNum непоследовательно определяется и используется.Я тоже не могу это исправить.

import tkinter as tk
from tkinter import ttk
import numpy as np

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
from matplotlib.figure import Figure
matplotlib.use("TkAgg")


class MainGUI(tk.Tk):

    def __init__(self):
        tk.Tk.__init__(self)
        self.title('Title')
        self.geometry('750x500')

        for i in range(9):
            self.rowconfigure(i, weight=1)
            self.columnconfigure(i, weight=1)

        self.mydict = {}
        self.patNum = ''

        self.makeTable()
        self.initializePlot()
        self.makeWidgets()

    def makeWidgets(self):
        self.Labels = []
        self.Entries = []
        self.labText = [('X Location:'), ('Y Location:')]

        self.xGrid = 1
        self.yGrid = int(np.ceil(len(self.labText) / self.xGrid))
        i = 0
        for j in range(0, self.xGrid + 1, 2):
            for k in range(self.yGrid):
                if(i == len(self.labText)):
                    break
                else:
                    self.label = tk.Label(self, text=self.labText[i])
                    self.label.grid(column=j, row=k + 8, sticky='SW')
                    self.Labels.append(self.label)
                    self.entry = tk.Entry(self)
                    self.entry.insert(0, '0.0000')
                    self.entry.grid(column=j + 1, row=k + 8, sticky='NS')
                    self.Entries.append(self.entry)
                    i += 1

        self.addBtn = tk.Button(self, text='Add Entry', command=self.addEntry)
        self.addBtn.grid(column=self.xGrid + 1, row=self.yGrid + 9, sticky='NSEW')
        self.delBtn = tk.Button(self, text='Delete Entry', command=self.delEntry)
        self.delBtn.grid(column=self.xGrid, row=self.yGrid + 9, sticky='NSEW')

    def makeTable(self):
        tab_header = ['Pattern #', 'Description']
        self.tree = ttk.Treeview(self, columns=tab_header, height=5, show="headings")
        self.tree.grid(column=0, row=0, columnspan=2, rowspan=5, sticky='NSEW')
        self.tree.heading(tab_header[0], text=tab_header[0].title())
        self.tree.heading(tab_header[1], text=tab_header[1].title())
        self.tree.insert('', 'end', values=("", "(new)"))
        self.tree.bind("<ButtonRelease-1>", self.getPtrnTable)
        self.counter = 1

    def getPtrnTable(self, event):
        self.item = self.tree.identify_row(event.y)
        try:
            self.patNum = self.tree.item(self.item, 'values')[0]
        except IndexError:
            self.patNum = ''
        self.patternPlot()

    def addEntry(self):
        check = (len(self.tree.get_children()) == 1)
        self.description = "Location: " + self.Entries[0].get() + ", " + self.Entries[1].get()
        self.tree.insert('', 'end', values=(self.counter, self.description))
        newEntry = [float(self.Entries[0].get()), float(self.Entries[1].get())]
        patKey = 'pattern_' + str(self.counter - 1)
        self.mydict[patKey] = newEntry
        if(check == True):
            self.points = np.array(newEntry, ndmin=2)
        else:
            self.points = np.vstack((self.points, newEntry))
        self.counter += 1
        self.scat.set_offsets(self.points)
        self.patNum = ""
        print("mydict: ", self.mydict)
        self.patternPlot()

    def delEntry(self):
        treeCnt = len(self.tree.get_children())
        if self.patNum:
            patKey = 'pattern_' + str(int(self.patNum) - 1)
            del self.mydict[patKey]
            items = self.tree.get_children()
            for i in range(int(self.patNum), treeCnt - 1):
                old_key = 'pattern_' + str(i)
                new_key = 'pattern_' + str(i - 1)
                self.mydict[new_key] = self.mydict.pop(old_key)
                self.tree.set(items[i], 'Description', self.tree.item(items[i + 1], 'values')[1])
            self.tree.delete(items[-1])
            print("mydict: ", self.mydict)
            self.plotAllPtrns()

    def initializePlot(self):
        self.fig = Figure(figsize=(1, 1), dpi=100)
        self.ptrnFig = self.fig.add_subplot(111)
        self.ptrnFig.axis([-5, 5, -5, 5])
        self.ptrnFig.spines['left'].set_position('zero')
        self.ptrnFig.spines['bottom'].set_position('zero')
        self.ptrnFig.grid(True)
        self.canvas = FigureCanvasTkAgg(self.fig, self)
        self.canvas.draw()
        self.canvas.get_tk_widget().grid(column=2, row=0, columnspan=6, rowspan=5, sticky='NSEW')
        self.scat = self.ptrnFig.scatter([], [], c="b", marker="o")
        self.scat2 = self.ptrnFig.scatter([], [], c="#ff5733", marker="o")  # Orange Color

    def plotAllPtrns(self):
        print(self.mydict)
        self.scat.remove()
        self.scat = self.ptrnFig.scatter([], [], c="b", marker="o", s=40)
        self.scat2.remove()
        self.scat2 = self.ptrnFig.scatter([], [], c="#ff5733", marker="o")  # Orange Color

        xy = []
        for k, v in self.mydict.items():
            xy.append([self.mydict[k][0], self.mydict[k][1]])
        if xy:
            self.scat.set_offsets(xy)
        self.canvas.draw_idle()

    def patternPlot(self):
        print(self.tree.get_children())
        # All data deleted - reinitialize plot
        if(len(self.tree.get_children()) == 1):
            print("One Child")
        # No specific entry selected - plot everything
        elif(self.patNum == ""):
            self.plotAllPtrns()
        # Specific entry chosen - highlight selected entry
        else:
            self.scat2.remove()
            self.scat2 = self.ptrnFig.scatter([], [], c="#ff5733", marker="o")  # Orange Color

            patKey = 'pattern_' + str(int(self.patNum) - 1)
            x = self.mydict[patKey][0]
            y = self.mydict[patKey][1]
            bolt = np.array([x, y], ndmin=2)

            self.scat2.set_offsets(bolt)
            self.canvas.draw_idle()


def main():
    MainGUI().mainloop()


if __name__ == '__main__':
    main()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...