I have a pyqt dialog that takes in a list of data that ends in a return (from a barcode scanner that has an auto return.)
The list is 5 items in length and currently I have to click "Add data" to run the populate_row method when 5 items are entered.
I split the string into a list using \n and deal with each item as needed.
Is there a way I can automate pressing the add data button when the number of lines or the number of \n reaches 5 in the QPlainTextEdit box?
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class Status(QDialog):
def __init__(self, parent=None):
super(Status, self).__init__(parent)
self.label = QLabel()
self.btn = QPushButton("Input Data")
self.btn.clicked.connect(self.populate_row)
self.layout = QVBoxLayout()
self.layout.addWidget(self.btn)
self.layout.addWidget(self.label)
self.resize(660, 260)
self.setLayout(self.layout)
def populate_row(self, letter):
self.dialog = QDialog()
self.dialog.resize(660, 260)
self.textBox = QPlainTextEdit(self.dialog)
Rbtn = QPushButton("Add Data")
Rbtn.clicked.connect(
lambda: self.enter_data(self.textBox.toPlainText()))
layout = QVBoxLayout(self.dialog)
layout.addWidget(self.textBox)
layout.addWidget(Rbtn)
self.dialog.exec_()
def enter_data(self, text):
self.label.setText(text)
lst = text.split("\n")
try:
for x in lst:
if x != "":
print(x)
self.do_something_with_x()
except IndexError:
pass
self.update_data()
self.dialog.close()
def do_something_with_x(self):
print('Something done with x..')
def update_data(self):
print('Data updated..')
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = Status()
ex.show()
sys.exit(app.exec_())
To count the line breaks use the blockCountChanged() signal, and if you want to click on the button you must use the click() method. Also to verify that a string is empty, it is enough with if x: since the strings are iterable and the if iterable returns False if it is empty, and True in other cases.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Status(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Status, self).__init__(parent)
self.label = QtWidgets.QLabel()
self.btn = QtWidgets.QPushButton("Input Data")
self.btn.clicked.connect(self.populate_row)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.btn)
layout.addWidget(self.label)
self.resize(660, 260)
def populate_row(self, letter):
self.dialog = QtWidgets.QDialog()
self.dialog.resize(660, 260)
self.textBox = QtWidgets.QPlainTextEdit()
self.textBox.blockCountChanged.connect(self.blockCount)
self.Rbtn = QtWidgets.QPushButton("Add Data")
self.Rbtn.clicked.connect(self.runcode)
layout = QtWidgets.QVBoxLayout(self.dialog)
layout.addWidget(self.textBox)
layout.addWidget(self.Rbtn)
self.dialog.exec_()
#QtCore.pyqtSlot(int)
def blockCount(self, num):
if num > 5: self.Rbtn.click()
def runcode(self):
self.enter_data(self.textBox.toPlainText())
def enter_data(self, text):
self.label.setText(text)
for x in text.split("\n"):
if x:
print(x)
self.do_something_with_x()
self.update_data()
self.dialog.close()
def do_something_with_x(self):
print('Something done with x..')
def update_data(self):
print('Data updated..')
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = Status()
ex.show()
sys.exit(app.exec_())
void QPlainTextEdit::blockCountChanged(int newBlockCount)
This signal is emitted whenever the block count changes. The new block count is passed in newBlockCount.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class Status(QDialog):
def __init__(self, parent=None):
super(Status, self).__init__(parent)
self.label = QLabel()
self.btn = QPushButton("Input Data")
self.btn.clicked.connect(self.populate_row)
self.layout = QVBoxLayout()
self.layout.addWidget(self.btn)
self.layout.addWidget(self.label)
self.resize(660, 260)
self.setLayout(self.layout)
def populate_row(self, letter):
self.dialog = QDialog()
self.dialog.resize(660, 260)
self.textBox = QPlainTextEdit(self.dialog)
#
self.textBox.blockCountChanged[int].connect(self.blockCount) # +++
Rbtn = QPushButton("Add Data")
Rbtn.clicked.connect(
lambda: self.enter_data(self.textBox.toPlainText()))
layout = QVBoxLayout(self.dialog)
layout.addWidget(self.textBox)
#layout.addWidget(Rbtn) # ---
self.dialog.exec_()
## +++
def blockCount(self, num):
if num > 5:
self.enter_data(self.textBox.toPlainText())
##
def enter_data(self, text):
self.label.setText(text)
lst = text.split("\n")
try:
for x in lst:
if x != "":
print(x)
self.do_something_with_x()
except IndexError:
pass
self.update_data()
self.dialog.close()
def do_something_with_x(self):
print('Something done with x..')
def update_data(self):
print('Data updated..')
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = Status()
ex.show()
sys.exit(app.exec_())
Related
I write a program to that send and recive data from serial, but I have a problem, I want to create a function "connect()" or a class, and when I press a button, the function is executed, but if I create this function in "MainWindow" class, variable "ser" from "TestThread" class become uninitialized, can you help me?
import sys
import serial
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.uic import loadUi
ser = serial.Serial('/dev/tty.usbmodem14201', 9600, timeout=1)
class TestThread(QThread):
serialUpdate = pyqtSignal(str)
def run(self):
while ser.is_open:
QThread.sleep(1)
value = ser.readline().decode('ascii')
self.serialUpdate.emit(value)
ser.flush()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
loadUi('/Users/bogdanvesa/P2A_GUI/mainwindow.ui', self)
self.thread = TestThread(self)
self.thread.serialUpdate.connect(self.handleSerialUpdate)
self.connect_btn.clicked.connect(self.connectSer)
self.lcd_EBtn.clicked.connect(self.startThread)
def startThread(self):
self.thread.start()
def handleSerialUpdate(self, value):
print(value)
self.lcd_lineEdit.setText(value)
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Instead of using pySerial + thread it is better to use QSerialPort that is made to live with the Qt event-loop:
from PyQt5 import QtCore, QtWidgets, QtSerialPort
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.message_le = QtWidgets.QLineEdit()
self.send_btn = QtWidgets.QPushButton(
text="Send",
clicked=self.send
)
self.output_te = QtWidgets.QTextEdit(readOnly=True)
self.button = QtWidgets.QPushButton(
text="Connect",
checkable=True,
toggled=self.on_toggled
)
lay = QtWidgets.QVBoxLayout(self)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(self.message_le)
hlay.addWidget(self.send_btn)
lay.addLayout(hlay)
lay.addWidget(self.output_te)
lay.addWidget(self.button)
self.serial = QtSerialPort.QSerialPort(
'/dev/tty.usbmodem14201',
baudRate=QtSerialPort.QSerialPort.Baud9600,
readyRead=self.receive
)
#QtCore.pyqtSlot()
def receive(self):
while self.serial.canReadLine():
text = self.serial.readLine().data().decode()
text = text.rstrip('\r\n')
self.output_te.append(text)
#QtCore.pyqtSlot()
def send(self):
self.serial.write(self.message_le.text().encode())
#QtCore.pyqtSlot(bool)
def on_toggled(self, checked):
self.button.setText("Disconnect" if checked else "Connect")
if checked:
if not self.serial.isOpen():
if not self.serial.open(QtCore.QIODevice.ReadWrite):
self.button.setChecked(False)
else:
self.serial.close()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I have used the above code redesigned, so it has a mainwindow with a menubar and a statusbar. I also added the QSerialPortInfo class. This version will find active ports and display them on the status bar.
I only tested this on rpi 4 and Windows 10.
import sys
from PyQt5 import QtCore, QtWidgets, QtSerialPort
from PyQt5.QtWidgets import QApplication, QMainWindow ,QWidget ,QToolBar ,QHBoxLayout, QAction ,QStatusBar ,QLineEdit ,QPushButton ,QTextEdit , QVBoxLayout
from PyQt5.QtCore import Qt , pyqtSignal
from PyQt5.QtSerialPort import QSerialPortInfo
class AddComport(QMainWindow):
porttnavn = pyqtSignal(str)
def __init__(self, parent , menu):
super().__init__(parent)
menuComporte = menu.addMenu("Comporte")
info_list = QSerialPortInfo()
serial_list = info_list.availablePorts()
serial_ports = [port.portName() for port in serial_list]
if(len(serial_ports)> 0):
antalporte = len(serial_ports)
antal = 0
while antal < antalporte:
button_action = QAction(serial_ports[antal], self)
txt = serial_ports[antal]
portinfo = QSerialPortInfo(txt)
buttoninfotxt = " Ingen informationer"
if portinfo.hasProductIdentifier():
buttoninfotxt = ("Produkt specifikation = " + str(portinfo.vendorIdentifier()))
if portinfo.hasVendorIdentifier():
buttoninfotxt = buttoninfotxt + (" Fremstillers id = "+ str(portinfo.productIdentifier()))
button_action = QAction( txt , self)
button_action.setStatusTip( buttoninfotxt)
button_action.triggered.connect(lambda checked, txt = txt: self.valgAfComportClick(txt))
menuComporte.addAction(button_action)
antal = antal +1
else:
print("Ingen com porte fundet")
def valgAfComportClick(self , port):
self.porttnavn.emit(port)
def closeEvent(self, event):
self.close()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
portname = "None"
self.setStatusBar(QStatusBar(self))
menu = self.menuBar()
comfinder = AddComport(self , menu)
comfinder.porttnavn.connect(self.valgAfComport)
self.setWindowTitle("Serial port display / send")
self.message_le = QLineEdit()
self.send_btn = QPushButton(
text="Send",
clicked=self.send
)
self.output_te = QTextEdit(readOnly=True)
self.button = QPushButton(
text="Connect",
checkable=True,
toggled=self.on_toggled
)
lay = QVBoxLayout(self)
hlay = QHBoxLayout()
hlay.addWidget(self.message_le)
hlay.addWidget(self.send_btn)
lay.addLayout(hlay)
lay.addWidget(self.output_te)
lay.addWidget(self.button)
widget = QWidget()
widget.setLayout(lay)
self.setCentralWidget(widget)
self.serial = QtSerialPort.QSerialPort(
portname,
baudRate=QtSerialPort.QSerialPort.Baud9600,
readyRead=self.receive)
#QtCore.pyqtSlot()
def receive(self):
while self.serial.canReadLine():
text = self.serial.readLine().data().decode()
text = text.rstrip('\r\n')
self.output_te.append(text)
#QtCore.pyqtSlot()
def send(self):
self.serial.write(self.message_le.text().encode())
#QtCore.pyqtSlot(bool)
def on_toggled(self, checked):
self.button.setText("Disconnect" if checked else "Connect")
if checked:
if not self.serial.isOpen():
self.serial.open(QtCore.QIODevice.ReadWrite)
if not self.serial.isOpen():
self.button.setChecked(False)
else:
self.button.setChecked(False)
else:
self.serial.close()
def valgAfComport(self , nyport):
seropen = False
if self.serial.isOpen():
seropen = True
self.serial.close()
self.serial.setPortName(nyport)
if seropen:
self.serial.open(QtCore.QIODevice.ReadWrite)
if not self.serial.isOpen():
self.button.setChecked(False)
print(nyport)
def closeEvent(self, event):
self.serial.close()
print("Comport lukket")
# print(comporttxt)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
There is this QLineEdit with setText is set to a predefined value and there is a QDialog with QLabel in it which is supposed to show whatever is in the QLineEdit. The code below shows the situation.
import sys
import os
import datetime
from PySide2.QtWidgets import *
from PySide2 import *
now = datetime.datetime.now()
now_str = now.strftime("%H.%M.%S,%d/%m/%y")
default_text = (str("Sugar_" + now_str))
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(600, 500)
btn = QPushButton("show dialog")
mw_layout = QVBoxLayout()
mw_layout.addWidget(btn)
self.setLayout(mw_layout)
btn.clicked.connect(show_d)
class dialog(QDialog):
def __init__(self):
super(dialog, self).__init__()
self.resize(400, 350)
title = QLineEdit()
title.setText(default_text)
show_title = QPushButton("Show title")
cancel = QPushButton("Cancel")
d_layout = QVBoxLayout()
d_layout.addWidget(title)
d_layout.addWidget(show_title)
d_layout.addWidget(cancel)
self.setLayout(d_layout)
t = title.text()
title_dialog = QDialog()
label = QLabel()
label.setText("The title is " + title.text())
ok = QPushButton("OK!")
t_layout = QVBoxLayout()
t_layout.addWidget(label)
t_layout.addWidget(ok)
title_dialog.setLayout(t_layout)
def show_t():
title_dialog.exec_()
title_dialog.setModal(True)
def close_t():
title_dialog.accept()
show_title.clicked.connect(show_t)
ok.clicked.connect(close_t)
cancel.clicked.connect(self.close_d)
def close_d(self):
self.reject()
def show_d():
d = dialog()
d.exec_()
d.setModal(True)
if __name__ == '__main__':
app = QApplication(sys.argv)
MainWindow = MainWindow()
MainWindow.show()
sys.exit(app.exec_())
But this doesn't work like I expected it to. The QLabel text just show the default text even when the text in QLineEdit is changed.
The console also shows the following error;
qt.xkb.compose: failed to create compose table.
I think there is something's obviously wrong but I can't seem to find what.
Any help appreciated.
You must update the text before a certain event, for example an instant before displaying the dialog:
class dialog(QDialog):
def __init__(self):
super(dialog, self).__init__()
self.resize(400, 350)
self.title_lineedit = QLineEdit(default_text)
show_title = QPushButton("Show title")
cancel = QPushButton("Cancel")
d_layout = QVBoxLayout(self)
d_layout.addWidget(self.title_lineedit)
d_layout.addWidget(show_title)
d_layout.addWidget(cancel)
self.title_dialog = QDialog()
self._title_label = QLabel()
ok = QPushButton("OK!")
t_layout = QVBoxLayout(self.title_dialog)
t_layout.addWidget(self._title_label)
t_layout.addWidget(ok)
show_title.clicked.connect(self.on_clicked)
ok.clicked.connect(self.title_dialog.reject)
cancel.clicked.connect(self.reject)
self.update_label()
def update_label(self):
self._title_label.setText("The title is " + self.title_lineedit.text())
def on_clicked(self):
self.update_label()
self.title_dialog.exec_()
I want get more than one input text from user in PyQt5.QtWidgets QInputDialog ... in this code I can just get one input text box and I want get more input text box when I was clicked the button. See the picture to more information ...
from PyQt5.QtWidgets import (QApplication,QWidget,QPushButton,QLineEdit,QInputDialog,QHBoxLayout)
import sys
class FD(QWidget):
def __init__(self):
super().__init__()
self.mysf()
def mysf(self):
hbox = QHBoxLayout()
self.btn = QPushButton('ClickMe',self)
self.btn.clicked.connect(self.sd)
hbox.addWidget(self.btn)
hbox.addStretch(1)
self.le = QLineEdit(self)
hbox.addWidget(self.le)
self.setLayout(hbox)
self.setWindowTitle("InputDialog")
self.setGeometry(300,300,290,150)
self.show()
def sd(self):
text , ok = QInputDialog.getText(self,'InputDialog','EnterYourName = ')
if ok:
self.le.setText(str(text))
if __name__ == '__main__':
app = QApplication(sys.argv)
F = FD()
sys.exit(app.exec_())
QInputDialog is a convenience class to retrieve a single input from the user.
If you want more fields, use a QDialog.
For example:
class InputDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.first = QLineEdit(self)
self.second = QLineEdit(self)
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self);
layout = QFormLayout(self)
layout.addRow("First text", self.first)
layout.addRow("Second text", self.second)
layout.addWidget(buttonBox)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
def getInputs(self):
return (self.first.text(), self.second.text())
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
dialog = InputDialog()
if dialog.exec():
print(dialog.getInputs())
exit(0)
Here's is a general solution, which accepts any number of inputs, based on Dimitry Ernot's answer.
from PyQt5.QtWidgets import QApplication, QLineEdit, QDialogButtonBox, QFormLayout, QDialog
from typing import List
class InputDialog(QDialog):
def __init__(self, labels:List[str], parent=None):
super().__init__(parent)
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self)
layout = QFormLayout(self)
self.inputs = []
for lab in labels:
self.inputs.append(QLineEdit(self))
layout.addRow(lab, self.inputs[-1])
layout.addWidget(buttonBox)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
def getInputs(self):
return tuple(input.text() for input in self.inputs)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
dialog = InputDialog(labels=["First","Second","Third","Fourth"])
if dialog.exec():
print(dialog.getInputs())
exit(0)
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.tabs()
def home(self):
df = QtGui.QPushButton('hello', self)
df.show()
def series(self):
df = QtGui.QCheckBox('hello', self)
df.show()
def tabs(self):
btn_home = QtGui.QPushButton(QtGui.QIcon('home.png'), 'Home', self)
btn_home.clicked.connect(self.home)
btn_series = QtGui.QPushButton(QtGui.QIcon('series.png'),'Series', self)
btn_series.clicked.connect(self.series)
self.show()
def run():
app = QtGui.QApplication(sys.argv)
GUI = Window()
sys.exit(app.exec_())
if __name__ == '__main__': run()
I wanted to delete the widgets shown from home module when i click series button and delete widgets from series module when i click home button.
So far whats happening is when i click series button he previous widgets from home module are still there.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sys
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.widget =QWidget()
self.layout = QHBoxLayout()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
self.tabs()
def home(self):
self.clear()
self.df1 = QPushButton('hello')
self.layout.addWidget(self.df1)
def series(self):
self.clear()
self.df2 = QCheckBox('hello')
self.layout.addWidget(self.df2)
def tabs(self):
self.btn_home = QPushButton(QIcon('home.png'), 'Home')
self.btn_home.clicked.connect(self.home)
self.layout.addWidget(self.btn_home)
self.btn_series = QPushButton(QIcon('series.png'),'Series')
self.btn_series.clicked.connect(self.series)
self.layout.addWidget(self.btn_series)
self.show()
def clear(self):
item = self.layout.itemAt(2)
if item != None :
widget = item.widget()
if widget != None:
self.layout.removeWidget(widget)
widget.deleteLater()
def run():
app = QApplication(sys.argv)
GUI = Window()
sys.exit(app.exec_())
if __name__ == '__main__': run()
My version is
self.main_canvas.children().remove(cogmapui)
cogmapui.deleteLater()
I checked it by putting a print("Deleted") in the cogmapui's __del__ function and, yes, it gets called.
Actual Code(posted on stack overflow) is pasted underneath . What i am trying is to remove the Start button and control status of the progressbar from a function inside some other class .
import sys, time
from PyQt4 import QtGui, QtCore
class ProgressBar(QtGui.QWidget):
def __init__(self, parent=None, total=20):
super(ProgressBar, self).__init__(parent)
self.progressbar = QtGui.QProgressBar()
self.progressbar.setMinimum(1)
self.progressbar.setMaximum(total)
self.button = QtGui.QPushButton('Start')
self.button.clicked.connect(self.handleButton)
main_layout = QtGui.QGridLayout()
main_layout.addWidget(self.button, 0, 0)
main_layout.addWidget(self.progressbar, 0, 1)
self.setLayout(main_layout)
self.setWindowTitle('Progress')
self._active = False
def handleButton(self):
if not self._active:
self._active = True
self.button.setText('Stop')
if self.progressbar.value() == self.progressbar.maximum():
self.progressbar.reset()
QtCore.QTimer.singleShot(0, self.startLoop)
else:
self._active = False
def closeEvent(self, event):
self._active = False
def startLoop(self):
while True:
time.sleep(0.05)
value = self.progressbar.value() + 1
self.progressbar.setValue(value)
QtGui.qApp.processEvents()
if (not self._active or
value >= self.progressbar.maximum()):
break
self.close()
app = QtGui.QApplication(sys.argv)
bar = ProgressBar(total=101)
bar.show()
sys.exit(app.exec_())
So i created a new class and replaced last few lines :
class drive():
app = QtGui.QApplication(sys.argv)
bar = ProgressBar(total=101)
bar.show()
sys.exit(app.exec_())
def control_progressbar(..):
...
a=drive()
Through function control_progressbar i want to modify the progressbar.