Python Forum
Can tkinter monitor dictionary
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Can tkinter monitor dictionary
#1
I am new to tkinter and was wondering if there is a way to add a simple block of code to monitor the contents of a dictionary and update a widget when that dictionary changes or do I need to add code everywhere I add or delete records from this dictionary.
Reply
#2
Look into after() for auto updates
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#3
I guess you want to change the minimal amount of code to your existing program. It could be as simple as using a subclass of dict for the variable that you are observing. For example with the MyDict class below, all the changes to the dictionary will trigger a printed message. You don't need to change anything to the existing code. Just inject the MyDict object where it is needed.

You can then update a GUI instead of printing messages.
__version__ = '2020.06.21'
sentinel = object()

class Observer:
    def on_item_added(self, k, v):
        print('added item', k, v)

    def on_value_changed(self, k, v, old):
        print('value changed', k, v, old)

    def on_item_removed(self, k, v):
        print('item removed', k, v)

    def on_cleared(self):
        print('dict cleared')
    

class MyDict(dict, Observer):
    def __setitem__(self, k, v):
        w = super().get(k, sentinel)
        super().__setitem__(k, v)
        if w is sentinel:
            self.on_item_added(k, v)
        else:
            self.on_value_changed(k, v, w)

    def __delitem__(self, k):
        self.pop(k)

    def clear(self):
        super().clear()
        self.on_cleared()

    def pop(self, k, d=sentinel):
        try:
            v = super().pop(k)
        except ValueError:
            if d is sentinel:
                raise
            else:
                return d
        else:
            self.on_item_removed(k, v)
            return v

    def popitem(self):
        k, v = super().popitem()
        self.on_item_removed(k, v)
        return (k, v)
    
    def update(self, *args, **kwargs):
        if args:
            if len(args) != 1:
                raise TypeError(
                    'update expected at most 1 argument, got', len(args))
            E = args[0]
            if hasattr(E, 'keys'):
                for k in E:
                    self[k] = E[k]
            else:
                for k, v in E:
                    self[k] = v
        for k in kwargs:
            self[k] = kwargs[k]

    def setdefault(self, k, d=None):
        if k not in self:
            self[k] = d
        return self[k]
 
if __name__ == '__main__':
    d = MyDict()
    # This code is exactly the same as in an ordinary dictionary.
    d[3] = "foo"
    d.update({4:'bar', 5:'spam'})
    del d[4]
    d.clear()
    d.setdefault(8, 'eggs')
    d[8] = 9
Output:
added item 3 foo added item 4 bar added item 5 spam item removed 4 bar dict cleared added item 8 eggs value changed 8 9 eggs
Reply
#4
thanks to both of you.. this is very helpful and will try both of these. Yes I want to keep the code as original as possible and maybe even just turn this on when I enable a higher level of loggin
Reply
#5
I changed the above code for a bugfix in version 2020.06.21
Reply
#6
All of this makes sense but not sure how to integrate this with tkinter. As often with my python learning, I can do okay with some more difficult stuff but get hung up on something that should be simple. From what I am reading tkinter doesn't write to gui until you run mainloop() but that then causes the rest of the code to not execute as the app.mainloop() line of code it doesn't get past..


if __name__ == '__main__':
    root = tk.Tk()
    app = Application(master=root)
    app.mainloop()
    print("did you get here?")

    d = MyDict()
    # This code is exactly the same as in an ordinary dictionary.
    d[3] = "foo"
    d.update({4: 'bar', 5: 'spam'})
    del d[4]
    d.clear()
    d.setdefault(8, 'eggs')
    d[8] = 9


Ultimately my __main__ code looks like this as it fires off two threads and I really the code in the threads to update tkinter. Any advice for working in these threads?
	t2 = ProcessReport()
	t2.start()

	t1 = RequestReport(t2)
	t1.start()
Reply
#7
If you just want to monitor the contents of a dictionary in a program and you don't want to make many changes to the program and this is maybe only for debugging or something, I would write the dictionary to a json file. You could make a special dictionary that logs changes, or you could set up a timer to periodically write the dictionary. Provide a way in your program tor turn the logging on and off.

Write a separate program to display the contents of the json file.. This way you don't have to tack on tkinter just to do something you aren't even interested in doing.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020