J. D.
J. D.

Reputation: 171

PyQt: Adding user input into list

I am trying to make a list box where a user can enter more items to the list. I have a list box set up with an add button. The add button opens up a user input box where it will direct the user to enter a value. However, I am have issue with passing on the value of user input to the add onto the list. Any suggestions would help. Below is my code:

List box

from input_box import *

class list_form(QtGui.QWidget):

    def __init__(self,list_of_items,open_text,parent= None):
        super(list_form, self).__init__()

        global output_path
        output_path = output_path_i

        grid = QtGui.QGridLayout()
        grid.setSpacing(10)
        self.widget = QtGui.QWidget()

        self.layout = QtGui.QGridLayout(self.widget)
        open_message = QtGui.QLabel(open_text)
        grid.addWidget(open_message,0,0,2,4)

        self.lst = QtGui.QListWidget()
        grid.addWidget(self.lst,3, 0,1,4)

        for i in list_of_items:
            self.lst.addItem(str(i))

        self.setLayout(grid)

        add = QtGui.QPushButton('Add')
        grid.addWidget(add,50,0)
        add.clicked.connect(self.add_button)

    def add_button(self):
        self.input_box = input_box()
        self.input_box.setWindowTitle("Window 2")
        self.input_box.show()

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = list_form(list(xrange(100)),"List of Values")
    window.setWindowTitle('Window 1')
    window.show()
    sip.setdestroyonexit(False)
    sys.exit(app.exec_())

Input box

import sys
from PyQt4 import QtCore, QtGui
import sip

class input_box(QtGui.QWidget):

    def __init__(self,parent= None):
        super(input_box, self).__init__()

        grid = QtGui.QGridLayout()
        grid.setSpacing(10)
        self.widget = QtGui.QWidget()

        self.layout = QtGui.QGridLayout(self.widget)
        open_message = QtGui.QLabel("Enter Value:")
        grid.addWidget(open_message,0,0,2,3)

        self.txt = QtGui.QLineEdit()
        grid.addWidget(self.txt,2, 0,1,2)

        self.setLayout(grid)

        save = QtGui.QPushButton('Save')
        grid.addWidget(save,50,0)
        a = save.clicked.connect(self.save)
        save.clicked.connect(self.close)

        cancel = QtGui.QPushButton('Cancel')
        grid.addWidget(cancel,50,1)
        cancel.clicked.connect(self.close)


    def save(self):
        value = self.txt.text()
        return value

Upvotes: 1

Views: 1703

Answers (2)

eyllanesc
eyllanesc

Reputation: 244003

If you want to get data from a window you should use a QDialog instead of a QWidget, connect the clicked signal of save and cancel to the accept and reject slot, respectively:

input_box.py

from PyQt4 import QtCore, QtGui

class Input_Box(QtGui.QDialog):
    def __init__(self,parent= None):
        super(Input_Box, self).__init__(parent)

        open_message = QtGui.QLabel("Enter Value:")
        self.txt = QtGui.QLineEdit()
        save = QtGui.QPushButton('Save', clicked=self.accept)
        cancel = QtGui.QPushButton('Cancel', clicked=self.reject)

        grid = QtGui.QGridLayout(self)
        grid.setSpacing(10)
        grid.addWidget(open_message, 0, 0)
        grid.addWidget(self.txt, 1, 0, 1, 2)
        grid.addWidget(save, 2, 0)
        grid.addWidget(cancel, 2, 1)
        self.setFixedSize(self.sizeHint())

    def save(self):
        value = self.txt.text()
        return value

Then you use the exec_() method that returns a code if it is called accept or reject, and according to that the data must be obtained and added. On the other hand, do not use global variables because they are a headache when debugging.

from PyQt4 import QtCore, QtGui
from input_box import Input_Box

class list_form(QtGui.QWidget):
    def __init__(self,list_of_items,open_text,parent= None):
        super(list_form, self).__init__()

        open_message = QtGui.QLabel(open_text)
        self.lst = QtGui.QListWidget()
        self.lst.addItems([str(i) for i in list_of_items])
        add = QtGui.QPushButton('Add', clicked=self.add_button)
        grid = QtGui.QGridLayout(self)
        grid.setSpacing(10)
        grid.addWidget(open_message)
        grid.addWidget(self.lst)
        grid.addWidget(add)

    @QtCore.pyqtSlot()
    def add_button(self):
        input_box = Input_Box()
        input_box.setWindowTitle("Window 2")
        if input_box.exec_() == QtGui.QDialog.Accepted:
            val = input_box.save()
            it = QtGui.QListWidgetItem(val)
            self.lst.addItem(it)
            self.lst.scrollToItem(it)

if __name__ == "__main__":
    import sys
    import sip
    app = QtGui.QApplication(sys.argv)
    window = list_form(list(range(100)),"List of Values")
    window.setWindowTitle('Window 1')
    window.show()
    sip.setdestroyonexit(False)
    sys.exit(app.exec_())

The advantage of using QDialog is the decoupling between the classes, for example there is no need to pass a QListWidget as the other answer suggests making it possible that you can use the same dialog for other purposes.

Upvotes: 2

strubbly
strubbly

Reputation: 3477

You need to arrange for the action taken when the save button is pressed to pass information back to the list widget. There's more than one way to do it, but just returning the data won't get it done.

Here's an example of the sort of thing that will work - though other approaches are possible.

Change the input_box constructor so it keeps a reference to the list widget which it expects:

class input_box(QtGui.QWidget):

    def __init__(self, list_widget, parent= None):
        super(input_box, self).__init__()
        self.list_widget = list_widget
        ...

Change the call to the constructor to provide that information:

    def add_button(self):
        self.input_box = input_box(self.lst)
        self.input_box.setWindowTitle("Window 2")
        self.input_box.show()

And then use that information in the save method to add to the list widget:

    def save(self):
        value = self.txt.text()
        self.list_widget.addItem(value)

Bingo!

An alternative approach could be to arrange for the input_box to emit a signal with the new value in it, and connect that to a slot on the list_form, or on the list_widget. Or in the input_box you could navigate via its parent to the list_widget. But I think mine is simple and straightforward.

Upvotes: 0

Related Questions