0

Backstory:

I'm currently developing a program that has several qComboBox and qLineEdit elements that a user can enter data into or select a value. When the user selects "New File" or "Open File" I want to check if any values have changed, and present the user with the option to save their work. The output file is XML.

Problem:

Basically, I just need to know if any of the values are different than the default values. I'm not concerned with what exact values are different, I just need to know that they are different.

I have tried using xmldiff by creating an Element Tree that contains the initial values when the program starts, then comparing that to a second Element Tree with the current values. It does not appear to be capable of just giving a true or false value, and the second Element Tree varies in size so I don't think I can simply do a 1 for 1 comparison.

The second thing I tried was simply setting a boolean value when the element changed, but I couldn't account for an element being reset to a default value. For example if a qLineEdit box had no value, and a user input something, that would set the boolean value to "true"; however, if they went back in and changed that value back to the default value the result would be "true" as well.

I was wondering if there is a "best practice" for doing this type of thing, or if someone could point me in the right direction. This seems like it should be trivial for the most part, but I don't know how to approach this.

EDIT … Added example for second attempt.

import sys
from PyQt5.QtWidgets import *


class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()

        self.modified = False
        self.edit1 = QLineEdit()
        layout = QGridLayout(self)
        layout.addWidget(self.edit1)

        self.edit1.editingFinished.connect(self.valueChanged)

    def valueChanged(self):
        print('valueChanged Event')
        self.modified = True

    def closeEvent(self, event):
        if self.modified:
            prompt = QMessageBox.warning(
                self, 'Save Changes?',
                'This document has been modified.\n'
                'Do you want to save these changes?',
                QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.Cancel
        )

            if prompt == QMessageBox.Yes:
                event.accept()
            if prompt == QMessageBox.No:
                event.accept()
            if prompt == QMessageBox.Cancel:
                event.ignore()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 100, 300, 100)
    window.show()
    sys.exit(app.exec_())
artomason
  • 3,625
  • 5
  • 20
  • 43
  • If you were to provide a minimal example to play with and show the different things you actually tried (the key is to make it minimal), this would be a good question. – Mad Physicist Sep 07 '18 at 04:36
  • None of the dialogs or XML stuff really needs to be shown. Just make something minimally runnable. – Mad Physicist Sep 07 '18 at 04:37
  • I do not quite get why this gets so many close votes ... the question is clear, the code provided shows whats been done - and demonstrates the problem. if you enter smth into the textbox the popup onclose is shown, if you clear the textbox again it is still shown: *thats the **unwanted** behaviour*. There are several ways to implement what's wanted and the question is easily answerable - either in code or in convepts. – Patrick Artner Sep 07 '18 at 05:50
  • @PatrickArtner I recommend not only to think about the current state of the question but to understand that a question has a life cycle, that is, you are qualifying the current question and not the initial question, and maybe those who voted to close only saw the previous state without the MCVE for example. :) – eyllanesc Sep 07 '18 at 05:58
  • I think this is partially my fault. I'm trying to explain a complex problem that I have without writing a novel about what is going on. – artomason Sep 07 '18 at 19:41
  • https://stackoverflow.com/questions/7905380/testing-equivalence-of-xml-etree-elementtree might also be a solution too. – artomason Sep 07 '18 at 19:58

1 Answers1

0

Member-dict with defaults:

Create a dict with { qline1Name: "tata", qcombo: 2, ...}. It holds your instances as key and its inital value. On save check if any current value is different from what stored in it and act accordingly. You would have to differentiate a couple of classes used as input elements - for combobox the initial selected index could be used.

Derive / monkey patch / decorate:

You could derive / monkey patch / decorate your Q*-inputs and add a default_value property to them. Set it on creation and evaluate on save:

(Mis-)use existing fields:

QLineEdit has QLineEdit.placeholderText that you could use for the "defaultvalue" and simply compare it (unless your defaultText are spaces - they get nixx'ed).

A more generic approach would be to leverage the QWidget.whatsThis QString field. That way only one property can be used for all input elements and you can if isinstance(...): to cast it's QString value when comparing it to the actual one.

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • 2
    No need to reinvent the wheel or resort to hackery: Qt provides a generic [Undo/Redo Framework](https://doc.qt.io/qt-5/qundo.html) that is designed to solve exactly this kind of problem. – ekhumoro Sep 07 '18 at 13:33
  • I had considered using a dictionary for this, but I don't think that is going to be the most effective way to approach this. I'm also not trying to implement anything hackish into the code. I would like to keep things as clean and understandable as possible. @ekhumoro, I read up on the Undo/Redo Framework, but I'm not exactly sure how that would help with the problem. This wouldn't really be an issue if I could just simply do `xmlTree1 != xmlTree2` and get a true or false value based on the comparison. – artomason Sep 07 '18 at 19:45
  • I think what I'm mostly looking for is what the industry normal is for dealing with change detection during a program run. – artomason Sep 07 '18 at 19:46
  • @AaronTomason Are you sure you really read it? An undo stack is designed precisely for the purpose of dealing with change detection during a program run. – ekhumoro Sep 07 '18 at 21:19
  • @ekhumoro I saw what you were talking about, but I'm still messing around with a minimal program trying to figure it out. I have not used `QUndoStack` before and it seems like I can track if "change" has occurred using `isClean()` and settings flags appropriately on save to reset the state. I still don't know if I can use it to detect if a value is equal to the default value. It seems like this needs to be implemented into every change method. – artomason Sep 07 '18 at 22:03
  • @AaronTomason curious to see your solution if you can make it happen. I would have thought it would be difficult to differentiate/ "set" the initial state w/o that one being able to be undo'ed. – Patrick Artner Sep 08 '18 at 08:39
  • @PatrickArtner I have not had a chance the past few days to monkey around with it a bit more. If I get it working, I will let you know. So far, I don't think this will allow me to detect if a new value is equal to the default value. Each action is saved, so even know the default value is present, `QUndoStack` sees `action --> action --> action` and the `isClean()` function returns false. – artomason Sep 09 '18 at 13:59