View textfile in QListWidget, add and remove - python

I want to be able to add timestamps to a QListWidget instance and save this to a textfile. As well as view the items already in the textfile so the items are saved after program exit.
The code I have at the moment saves it to the list as I want to, but I do not see the items I added before closing and reopening the program:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from datetime import datetime
class feedingTime(QDialog):
def __init__(self):
QDialog.__init__(self)
layout = QVBoxLayout()
self.feedList = QListWidget()
self.label = QLabel(datetime.now().strftime('%Y-%m-%d %H:%M:%S'),self)
self.button = QPushButton("Add time")
self.info = QLabel("Baby was last fed:")
layout.addWidget(self.label)
layout.addWidget(self.button)
layout.addWidget(self.info)
layout.addWidget(self.feedList)
self.setLayout(layout)
self.timer = QTimer(self.label)
self.timer.setInterval(1000)
self.timer.timeout.connect(self.displayTime)
self.timer.start()
self.button.clicked.connect(self.feedAdd)
def feedAdd(self):
self.feedList.addItem(self.label.text())
def displayTime(self):
self.label.setText(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
if __name__ == "__main__":
app = QApplication(sys.argv)
dialog = feedingTime()
dialog.show()
sys.exit(app.exec_())
Is there and easy way to read list from textfile as well as appending new timestamps when button is pressed? It would also be nice to add a button that removes the "oldest" timestamp when clicked.
Im trying to make a brestfeeding app for my wife :)
PyQt noob here. Thanks for the help.

Saving data to a log has nothing to do with PyQt. All you need is basic knowledge of working with I/O in Python. I used simple log file which must be located in same directory as the script does (could be improved to something more sophisticated). I implemented also desired delete button, however, I'm not sure if I correctly understood meaning of "oldest" timestamp.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from datetime import datetime
FILENAME = "history.log"
class feedingTime(QDialog):
def __init__(self):
QDialog.__init__(self)
layout = QVBoxLayout()
self.feedList = QListWidget()
self.label = QLabel(datetime.now().strftime('%Y-%m-%d %H:%M:%S'),self)
self.button = QPushButton("Add time")
self.info = QLabel("Baby was last fed:")
self.buttonDelete = QPushButton("Delete oldest")
layout.addWidget(self.label)
layout.addWidget(self.button)
layout.addWidget(self.info)
layout.addWidget(self.feedList)
layout.addWidget(self.buttonDelete)
self.setLayout(layout)
self.timer = QTimer(self.label)
self.timer.setInterval(1000)
self.timer.timeout.connect(self.displayTime)
self.timer.start()
self.button.clicked.connect(self.feedAdd)
self.buttonDelete.clicked.connect(self.deleteOldest)
self.loadOldData()
def deleteOldest(self):
self.feedList.takeItem(self.feedList.count() - 1)
lines = open(FILENAME).readlines()
with open(FILENAME, 'w') as f:
f.writelines(lines[1:])
def feedAdd(self):
date = self.label.text()
self.feedList.addItem(date)
f = open(FILENAME, 'a')
f.write(date + '\n')
f.close()
self.feedList.sortItems(Qt.DescendingOrder) # change to AscendingOrder if want inverted order
def fillData(self, lines):
for line in lines:
self.feedList.addItem(line.rstrip())
def loadOldData(self):
try:
file = open(FILENAME)
lines = file.readlines()
self.fillData(lines)
except IOError:
print "File" + FILENAME + "not found, skipping..."
def displayTime(self):
self.label.setText(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
if __name__ == "__main__":
app = QApplication(sys.argv)
dialog = feedingTime()
dialog.show()
sys.exit(app.exec_())

Related

Getting value of all user inputs/lineEdits

I'm looking for a way to get the value or text of all the user inputs/lineedits of the app
I'm creating an app that creates multiple folders at once. There are multiple user inputs to write in the name of the folders, that I create by calling the same function multiple times. Using this "self.le2.text()" only gets the value of the first user input created.
Here's my code so far. Essentially I want the "create folders" button to create all the folders from all the user inputs created by the "new_folder" function. Right now its only getting the value of the first one created.
import sys
import os
from PySide6 import QtWidgets, QtCore, QtGui
from PySide6.QtWidgets import QApplication
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
#set central widget and layout
self.setWindowTitle("Amazing Folder Generator")
self.generalLayout = QtWidgets.QVBoxLayout()
self.centralWidget = QtWidgets.QWidget()
self.setCentralWidget(self.centralWidget)
self.centralWidget.setLayout(self.generalLayout)
self.le1 = QtWidgets.QLineEdit()
self.file_path()
self.button1 = QtWidgets.QPushButton("New Folder")
self.generalLayout.addWidget(self.button1)
self.button1.pressed.connect(self.new_folder)
self.button2 = QtWidgets.QPushButton("Create Folders")
self.generalLayout.addWidget(self.button2)
self.button2.pressed.connect(self.create_files)
self.new_folder()
self.new_folder()
self.new_folder()
def file_path(self):
self.le1 = QtWidgets.QLineEdit()
self.le1.setFixedHeight(20)
self.le1.setAlignment(QtCore.Qt.AlignRight)
self.path_layout = QtWidgets.QFormLayout()
self.path_layout.addRow("Folder Path: ", self.le1)
self.generalLayout.addLayout(self.path_layout)
def new_folder(self):
self.le2 = QtWidgets.QLineEdit()
self.le2.setFixedHeight(20)
self.le2.setAlignment(QtCore.Qt.AlignRight)
self.path_layout = QtWidgets.QFormLayout()
filename = "File: "
self.path_layout.addRow(filename, self.le2)
self.generalLayout.addLayout(self.path_layout)
def create_files(self):
path = self.le1.text() + "\\"
filename = self.le2.text()
newpath = path + filename
if not os.path.exists(newpath):
os.makedirs(newpath)
else:
print("folder already exists")
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The simple solution is to store the QLineEdits in a list and then iterate to get the text. In this case I have rewritten your code to make it more readable.
import sys
import os
import os.path
from PySide6 import QtWidgets, QtCore, QtGui
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowTitle("Amazing Folder Generator")
self.folder_le = QtWidgets.QLineEdit(alignment=QtCore.Qt.AlignRight)
self.folder_le.setFixedHeight(20)
self.add_row_button = QtWidgets.QPushButton("New Folder")
self.create_button = QtWidgets.QPushButton("Create Folders")
self.files_layout = QtWidgets.QFormLayout()
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
self.form_layout = QtWidgets.QFormLayout(central_widget)
self.form_layout.addRow("Folder Path:", self.folder_le)
self.form_layout.addRow(self.add_row_button)
self.form_layout.addRow(self.create_button)
self.form_layout.addRow(self.files_layout)
self._files_le = []
self.add_row_button.clicked.connect(self.add_row)
self.create_button.clicked.connect(self.create_files)
for _ in range(3):
self.add_row()
#property
def folder(self):
return self.folder_le.text()
#property
def filenames(self):
return [le.text() for le in self._files_le]
def add_row(self):
le = QtWidgets.QLineEdit(alignment=QtCore.Qt.AlignRight)
le.setFixedHeight(20)
self.files_layout.addRow("File:", le)
self._files_le.append(le)
def create_files(self):
for filename in self.filenames:
newpath = os.path.join(self.folder, filename)
if not os.path.exists(newpath):
os.makedirs(newpath)
else:
print("folder already exists")
def main():
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

QFileDialog.getOpenFileName change button text from 'Open' to 'Remove'

I am using QFileDialog.getOpenFileName(self,'Remove File', "path", '*.pdf') to select a file and get the path in order to remove it from my application. The issue is the QFileDialog.getOpenFileName window button says 'Open' when selecting a file which will be confusing to the user.
Is there any way to change the button text from 'Open' to 'Remove'/'Delete'
When using the static method QFileDialog::getOpenFileName() the first thing is to obtain the QFileDialog object and for that we use a QTimer and the findChild() method:
# ...
QtCore.QTimer.singleShot(0, self.on_timeout)
filename, _ = QtWidgets.QFileDialog.getOpenFileName(...,
options=QtWidgets.QFileDialog.DontUseNativeDialog)
def on_timeout(self):
dialog = self.findChild(QtWidgets.QFileDialog)
# ...
Then you can get the text iterating over the buttons until you get the button with the searched text and change it:
for btn in dialog.findChildren(QtWidgets.QPushButton):
if btn.text() == "&Open":
btn.setText("Remove")
That will work at the beginning but every time you interact with the QTreeView they show, update the text to the default value, so the same logic will have to be applied using the currentChanged signal from the selectionModel() of the QTreeView but for synchronization reasons it is necessary Update the text later using another QTimer.singleShot(), the following code is a workable example:
import sys
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button = QtWidgets.QPushButton("Press me")
button.clicked.connect(self.on_clicked)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button)
#QtCore.pyqtSlot()
def on_clicked(self):
QtCore.QTimer.singleShot(0, self.on_timeout)
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
"Remove File",
"path",
"*.pdf",
options=QtWidgets.QFileDialog.DontUseNativeDialog,
)
def on_timeout(self):
dialog = self.findChild(QtWidgets.QFileDialog)
dialog.findChild(QtWidgets.QTreeView).selectionModel().currentChanged.connect(
lambda: self.change_button_name(dialog)
)
self.change_button_name(dialog)
def change_button_name(self, dialog):
for btn in dialog.findChildren(QtWidgets.QPushButton):
if btn.text() == self.tr("&Open"):
QtCore.QTimer.singleShot(0, lambda btn=btn: btn.setText("Remove"))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
The first step can be avoided if the static method is not used and create the dialog using an instance of QFileDialog:
import sys
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button = QtWidgets.QPushButton("Press me")
button.clicked.connect(self.on_clicked)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button)
#QtCore.pyqtSlot()
def on_clicked(self):
dialog = QtWidgets.QFileDialog(
self,
"Remove File",
"path",
"*.pdf",
supportedSchemes=["file"],
options=QtWidgets.QFileDialog.DontUseNativeDialog,
)
self.change_button_name(dialog)
dialog.findChild(QtWidgets.QTreeView).selectionModel().currentChanged.connect(
lambda: self.change_button_name(dialog)
)
if dialog.exec_() == QtWidgets.QDialog.Accepted:
filename = dialog.selectedUrls()[0]
print(filename)
def change_button_name(self, dialog):
for btn in dialog.findChildren(QtWidgets.QPushButton):
if btn.text() == self.tr("&Open"):
QtCore.QTimer.singleShot(0, lambda btn=btn: btn.setText("Remove"))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
While I appreciate the solution provided by #eyllanesc, I'd like to propose a variation.
Under certain circumstances, the code for that answer might fail, specifically:
the delay that X11 suffers from mapping windows;
the checking of localized button strings;
the selection using the file name edit box;
Considering the above, I propose an alternate solution, based on the points above.
For obvious reasons, the main point remains: the dialog must be non-native.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class FileDialogTest(QWidget):
def __init__(self):
super().__init__()
layout = QHBoxLayout(self)
self.fileEdit = QLineEdit()
layout.addWidget(self.fileEdit)
self.selectBtn = QToolButton(icon=QIcon.fromTheme('folder'), text='…')
layout.addWidget(self.selectBtn)
self.selectBtn.clicked.connect(self.showSelectDialog)
def checkSelectDialog(self):
dialog = self.findChild(QFileDialog)
if not dialog.isVisible():
# wait for dialog finalization, as testOption might fail
return
# dialog found, stop the timer and delete it
self.sender().stop()
self.sender().deleteLater()
if not dialog.testOption(dialog.DontUseNativeDialog):
# native dialog, we cannot do anything!
return
def updateOpenButton():
selection = tree.selectionModel().selectedIndexes()
if selection and not tree.model().isDir(selection[0]):
# it's a file, change the button text
button.setText('Select my precious file')
tree = dialog.findChild(QTreeView)
button = dialog.findChild(QDialogButtonBox).button(
QDialogButtonBox.Open)
# check for selected files on open
updateOpenButton()
# connect the selection update signal
tree.selectionModel().selectionChanged.connect(
lambda: QTimer.singleShot(0, updateOpenButton))
def showSelectDialog(self):
QTimer(self, interval=10, timeout=self.checkSelectDialog).start()
path, filter = QFileDialog.getOpenFileName(self,
'Select file', '<path_to_file>',
"All Files (*);;Python Files (*.py);; PNG Files (*.png)",
options=QFileDialog.DontUseNativeDialog)
if path:
self.fileEdit.setText(path)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
ex = FileDialogTest()
ex.show()
sys.exit(app.exec())
Obviously, for PyQt6 you'll need to use the proper Enum namespaces (i.e. QFileDialog.Option.DontUseNativeDialog, etc.).

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
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.
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.

Text from QLineEdit not displaying

I am grabbing the user input from the line edit and displaying it on the QMessageBox but it won't show for some reason. I thought maybe I wasn't grabbing the input from QLineEdit at all but when I tried printing it on the terminal (it still wouldn't show there btw) the terminal scrolled down, recognizing that there is new data in it but just not displaying it. Get what I am saying?
import os
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
def main():
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
# create objects
label = QLabel(self.tr("enter the data "))
self.le = QLineEdit()
self.te = QTextEdit()
# layout
layout = QVBoxLayout(self)
layout.addWidget(label)
layout.addWidget(self.le)
layout.addWidget(self.te)
self.setLayout(layout)
# create connection
self.mytext = str(self.le.text())
self.connect(self.le, SIGNAL("returnPressed(void)"),
self.display)
def display(self):
QApplication.instance().processEvents()
msg = QMessageBox.about(self, 'msg', '%s' % self.mytext)
print(self.mytext)
self.te.append(self.mytext)
self.le.setText("")
if __name__ == "__main__":
main()
You are currently reading the QLineEdit in the constructor, and at that moment the QLineEdit is empty, you must do it in the slot:
def display(self):
mytext = self.le.text()
msg = QMessageBox.about(self, 'msg', '%s' % mytext)
self.te.append(mytext)
self.le.clear()
Note: use clear() to clean the QLineEdit

How to save and restore current state and values using PyQt4

I am trying to figure out a way to save the current state and all values in the gui such as the text in QLineEdit and QEditText widgets.
I found this code which I have been trying to use and it seems that I can get it to save everything okay when I exit the GUI but when I open it, all it seems to restore is the window dimensions if I had moved them previously.
I can see in the ini file that everything gets saved including any text in the 2 widgets but I cant get the text to restore when I open the GUI. Does anyone know how I can get the text values to restore as well?
Here is what I am currently working with.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
def restore(settings):
finfo = QFileInfo(settings.fileName())
if finfo.exists() and finfo.isFile():
for w in qApp.allWidgets():
mo = w.metaObject()
if w.objectName() != "":
for i in range(mo.propertyCount()):
name = mo.property(i).name()
val = settings.value("{}/{}".format(w.objectName(), name), w.property(name))
w.setProperty(name, val)
def save(settings):
for w in qApp.allWidgets():
mo = w.metaObject()
if w.objectName() != "":
for i in range(mo.propertyCount()):
name = mo.property(i).name()
settings.setValue("{}/{}".format(w.objectName(), name), w.property(name))
class MainWindow(QWidget):
settings = QSettings("gui.ini", QSettings.IniFormat)
def __init__(self):
super(MainWindow, self).__init__()
self.setObjectName("MainWindow")
restore(self.settings)
self.layout = QGridLayout()
self.text_Box = QTextEdit(self)
self.text_Box.setObjectName("text_Box")
self.layout.addWidget(self.text_Box, 2, 0, 1, 1)
self.quit_Button = QPushButton(self)
self.quit_Button.setMaximumSize(30, 30)
self.quit_Button.setObjectName("quit_Button")
self.layout.addWidget(self.quit_Button, 3, 0, 1, 1)
self.line_Edit = QLineEdit(self)
self.line_Edit.setObjectName("line_Edit")
self.layout.addWidget(self.line_Edit, 1, 0, 1, 1)
self.quit_Button.clicked.connect(self.exitGUI)
self.setLayout(self.layout)
def closeEvent(self, event):
save(self.settings)
QWidget.closeEvent(self, event)
def exitGUI(self):
self.close()
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
mainwindow = MainWindow()
mainwindow.show()
sys.exit(app.exec_())

Categories

Resources