i want to create QLabels dynamically at runtime, and change the Text afterwards.
I did it like that:
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.counter = 0
self.items = self.read_hosts()
self.layout = QGridLayout()
for item in self.items:
self.item = QLabel(item, self)
self.item.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.item.setAlignment(Qt.AlignCenter)
self.item.setStyleSheet("QLabel {background-color: green;}")
self.layout.addWidget(self.item,self.counter, 0)
self.counter += 1
self.setLayout(self.layout)
self.startWorker()
self.show()
def change_txt(self, lb, i):
self.item.setText("{}".format(i))
It won't work.
I understand why it would change just the text of the last label. I'm doing something wrong during assignment.
How can I create all labels completely variably and subsequently change the texts?
I am using:
PyQT5
on Windows 10
Thanks!
Here is my whole code:
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import os
class Worker(QObject):
update = pyqtSignal(str,int)
exception = pyqtSignal(str)
def read_hosts(self):
#try:
file = open("hosts.ini", "r")
return file.readlines()
#except Exception as ex:
#self.exception.emit(str(ex))
def check_ping(self):
#try:
hosts = self.read_hosts()
while True:
for host in hosts:
print(host)
params = " -l 1000"
response = os.system("ping " + host + params)
print("weiter")
self.update.emit(host, response)
#except Exception as ex:
#self.exception.emit(str(ex))
class Window(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.counter = 0
self.items = self.read_hosts()
self.layout = QGridLayout()
for item in self.items:
self.item = QLabel(item, self)
self.item.setObjectName("label" + str(self.counter))
print("label" + str(self.counter))
self.item.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.item.setAlignment(Qt.AlignCenter)
self.item.setStyleSheet("QLabel {background-color: green;}")
self.layout.addWidget(self.item,self.counter, 0)
self.counter += 1
self.setLayout(self.layout)
self.startWorker()
self.show()
def startWorker(self):
self.thread = QThread()
self.obj = Worker()
self.thread = QThread()
self.obj.moveToThread(self.thread)
self.obj.update.connect(self.onUpdate)
self.obj.exception.connect(self.onException)
self.thread.started.connect(self.obj.check_ping)
self.thread.start()
def read_hosts(self):
#try:
file = open("hosts.ini", "r")
return file.readlines()
#except Exception as ex:
#self.exception.emit(str(ex))
def onException(self, msg):
QMessageBox.warning(self, "Eine Exception im Worker wurde geworfen: ", msg)
def onUpdate(self, lb, i):
label = lb
self.label0.setText("{}".format(i))
app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())
hosts.ini:
192.168.1.1
192.168.1.2
192.168.1.30
I have improved your code in the following aspects:
If your layout has a column it is not necessary to use QGridLayout, just QVBoxLayout.
Having a read_hosts() method for each class is a waste, so I've created a unique function.
self.item is an attribute of the class that is continually being overwritten so it is not necessary to create them.
The objectName should be the name of the host, that is, the IP, since that is the information that the thread has.
To find the label through the objectName you can use findChild().
import sys
import os
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def read_hosts():
file = open("hosts.ini", "r")
return file.readlines()
class Worker(QObject):
update = pyqtSignal(str, int)
exception = pyqtSignal(str)
def check_ping(self):
hosts = read_hosts()
while True:
for host in hosts:
params = "-l 1000"
response = os.system("ping {} {}".format(host, params))
print("weiter")
self.update.emit(host, response)
class Window(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.layout = QVBoxLayout(self)
hosts = read_hosts()
for host in hosts:
label = QLabel(host)
label.setObjectName(host)
label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
label.setAlignment(Qt.AlignCenter)
label.setStyleSheet("QLabel {background-color: green;}")
self.layout.addWidget(label)
self.startWorker()
def startWorker(self):
self.thread = QThread()
self.obj = Worker()
self.thread = QThread()
self.obj.moveToThread(self.thread)
self.obj.update.connect(self.onUpdate)
self.obj.exception.connect(self.onException)
self.thread.started.connect(self.obj.check_ping)
self.thread.start()
def onException(self, msg):
QMessageBox.warning(self, "Eine Exception im Worker wurde geworfen: ", msg)
def onUpdate(self, host, value):
label = self.findChild(QLabel, host)
label.setText("{}".format(value))
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
Related
I want to turn the dial on pyqt's GUI to change parameters while sounddevice's outputstream is processing, but the GUI freezes. I've tried everything I could find, and I've pieced together a bunch of code, but I'd like to know what the solution is.
I want to change the equalizer of the music being played in near real time by changing the parameters of the pedalboard that grants the equalizer.
import sys
import time
from PyQt5.QtCore import QObject, QThread, pyqtSignal
from PyQt5.QtWidgets import *
from PyQt5.QtMultimedia import QSound
from pedalboard import *
import sounddevice as sd
import librosa
from threading import *
import threading
import soundfile as sf
def long_running_function(update_ui,board):
event = threading.Event()
try:
data, fs = sf.read('continue.wav', always_2d=True)
current_frame = 0
def callback(outdata, frames, time, status):
nonlocal current_frame
if status:
print(status)
chunksize = 2024
#print(chunksize, current_frame,len(data),frames)
outdata[:chunksize] = board(data[current_frame:current_frame + chunksize])
if chunksize < 2024:
outdata[chunksize:] = 0
raise sd.CallbackStop()
current_frame += chunksize
stream = sd.OutputStream(
samplerate=fs, blocksize=2024 , channels=data.shape[1],
callback=callback, finished_callback=event.set)
with stream:
event.wait()
except KeyboardInterrupt:
exit('\nInterrupted by user')
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(int)
def __init__(self, main_window):
self.main_window = main_window
super(Worker, self).__init__(main_window)
def run(self):
long_running_function(self.update_progress,self.main_window.make_board())
self.finished.emit()
def update_progress(self, percent):
self.progress.emit(percent)
class MainWindow(QMainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.progress = QProgressBar()
self.button = QPushButton("Start")
self.dial = QDial()
self.init_ui()
self.qsound = None
self.qsound2 = None
self.effected_audio = None
self.audio = None
self.sr = None
self.dial.valueChanged.connect(self.make_board)
self.button.clicked.connect(self.execute)
self.show()
def init_ui(self):
self.setGeometry(100, 100, 250, 250)
layout = QVBoxLayout()
layout.addWidget(self.progress)
layout.addWidget(self.button)
layout.addWidget(self.dial)
self.setWindowTitle('Audio Player')
button_play = QPushButton("環境音を再生")
button_play2 = QPushButton("音楽と環境音を再生")
button_stop = QPushButton("Stop")
button_dialog = QPushButton("音楽を選択")
button_dialog2 = QPushButton("環境音を選択")
dial = QDial()
live = QPushButton("LIVE")
self.label = QLabel(self)
self.label2 = QLabel(self)
layout.addWidget(button_dialog)
layout.addWidget(button_dialog2)
layout.addWidget(button_play)
layout.addWidget(button_stop)
layout.addWidget(button_play2)
layout.addWidget(live)
layout.addWidget(self.dial)
layout.addWidget(self.label)
layout.addWidget(self.label2)
button_dialog.clicked.connect(self.button_openfile)
button_dialog2.clicked.connect(self.button_openfile2)
button_play.clicked.connect(self.button_play)
button_play2.clicked.connect(self.button_play2)
button_stop.clicked.connect(self.button_stop)
live.clicked.connect(self.start)
self.dial.valueChanged.connect(self.sliderMoved)
self.dial.valueChanged.connect(self.make_board)
self.dial.setMinimum(0)
self.dial.setMaximum(20)
self.dial.setValue(5)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
def sliderMoved(self):
number = self.dial.value()
print(number)
return number
def make_board(self):
board = Pedalboard([
Compressor(ratio=10, threshold_db=-20),
Gain(gain_db=self.dial.value()),
Phaser(),
Reverb()
],sample_rate=44100)
return board
def button_play(self):
print("")
def button_play2(self):
if self.qsound is not None:
board = self.make_board(self.effects)
self.effected_audio = board(self.audio,self.sr)
sd.play(self.effected_audio)
self.qsound2.play()
def button_stop(self):
if self.qsound is not None:
sd.stop()
self.qsound2.stop()
def button_openfile(self):
filepath, _ = QFileDialog.getOpenFileName(self, 'Open file','c:\\',"Audio files (*.wav)")
filepath = os.path.abspath(filepath)
self.qsound = QSound(filepath)
self.filename = os.path.basename(filepath)
self.audio, self.sr = librosa.load(self.filename, sr=44100)
self.label.setText(self.filename)
self.label.adjustSize()
def button_openfile2(self):
filepath2, _ = QFileDialog.getOpenFileName(self, 'Open file','c:\\',"Audio files (*.wav)")
filepath2 = os.path.abspath(filepath2)
self.qsound2 = QSound(filepath2)
self.filename2 = os.path.basename(filepath2)
self.label2.setText(self.filename2)
self.label2.adjustSize()
def start(self):
self.thread.start()
def execute(self):
self.update_progress(0)
self.thread = QThread()
self.worker = Worker(self)
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.progress.connect(self.update_progress)
self.thread.start()
self.button.setEnabled(False)
def update_progress(self, progress):
self.progress.setValue(progress)
self.button.setEnabled(progress == 100)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
app.exec_()
Qt Docs for moveToThread() say:
Changes the thread affinity for this
object and its children. The object
cannot be moved if it has a parent.
I cannot see where Worker instance gets started. Maybe you wanted to derive Worker from QThread, and call self.worker.start()?
OutputStream and the Qt GUI both have their own internal main loops which are responsible for handling their respective events. When you execute both on the same thread, one or the other might freeze.
I'm writing an UI using QT5 and python, I added a thread to handle the UI, thread works "fine", a function inside the thread receive 2 strings and return 2 strings (I'm making experiments before develop the real project just to see how it works), as you can see in the code after call the thread function with:
self.requestConexion.emit('lblText1','dddddd')
I call another function that is just a simple counter
self.contador()
so I expect that before the counter finish the value of the control self.lblText1 change, but this is not happen... here is the main code:
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import UI_test
import time
import sys
class Threaded(QObject):
result=pyqtSignal(str,str)
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
#pyqtSlot(str,str)
def etiquetas(self,lbl,texto):
print(texto)
self.result.emit(lbl,texto)
class MainApp(QMainWindow, UI_test.Ui_MainWindow):
requestConexion=pyqtSignal(str,str)
def __init__(self, parent=None):
super(MainApp, self).__init__(parent)
self._thread=QThread()
self._threaded=Threaded(result=self.displayLabel)
self.requestConexion.connect(self._threaded.etiquetas)
self._threaded.moveToThread(self._thread)
qApp.aboutToQuit.connect(self._thread.quit)
self._thread.start()
self.setupUi(self)
self.btnStart.clicked.connect(self.conexion)
#pyqtSlot()
def conexion(self):
#self._thread.start()
print(1)
self.requestConexion.emit('lblText1','dddddd')
self.contador()
text, ok = QInputDialog.getText(self, 'Text Input Dialog', 'Enter your name:')
if ok:
print(str(text))
#pyqtSlot()
def contador(self):
i=0
while i<50:
print(i)
time.sleep(0.1)
i+=1
#pyqtSlot(str,str)
def displayLabel(self, etiqueta, texto):
self.lblText1.setText(etiqueta)
print(texto)
def main():
app = QApplication(sys.argv)
form = MainApp()
form.show()
app.exec_()
exit(app.exec_())
if __name__ == '__main__':
main()
any idea whats wrong?
I finally find the answer to my question in the next blog:
https://martinfitzpatrick.name/article/multithreading-pyqt-applications-with-qthreadpool/
this is a really great tutorial, after read the documentation in the blog I was able to modify one of the examples to modify the text of a label control in real time; here is the final code:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import time
class Worker(QRunnable):
def __init__(self, fn, *args, **kwargs):
super(Worker, self).__init__()
self.args = args
self.kwargs = kwargs
self.fn = fn
#pyqtSlot()
def run(self):
#print(self.args, self.kwargs)
print("Thread start")
time.sleep(0.2)
self.fn(*self.args, **self.kwargs) #ejecuta la funcion recibida
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.threadpool = QThreadPool()
print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())
self.counter = 0
layout = QVBoxLayout()
self.l = QLabel("Start")
self.l2 = QLabel("xxxxx")
b = QPushButton("DANGER!")
b.pressed.connect(self.oh_no)
layout.addWidget(self.l)
layout.addWidget(self.l2)
layout.addWidget(b)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
self.show()
self.etiqueta="rrrrrrrrrr"
self.timer = QTimer()
self.timer.setInterval(500)
self.timer.timeout.connect(self.recurring_timer)
self.timer.start()
def oh_no(self):
self.etiqueta="hhhhhhhhhhhhhhhhh"
worker = Worker(self.execute_this_fn,'4444')
self.threadpool.start(worker)
def recurring_timer(self):
self.counter +=1
self.l.setText("Counter: %d" % self.counter)
def execute_this_fn(self,x):
print("Hello!")
self.l2.setText(x)
app = QApplication([])
window = MainWindow()
app.exec_()
I made some received tcp packet dialog by pyqt5.
This code are load dialog, call receive tcp packet in qthread.
I want to send packet data to dialog.
How to send?
It's my code.
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import socketserver
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
try:
data = self.rfile.read(28)
# how to send packet data to dialog?
except Exception as e:
print('MyTCPHandler.handle exception error: ', e)
class TestThread(QThread):
HOST, PORT = '192.168.0.100', 8484
def __init__(self, parent=None):
super().__init__()
def receive_packet(self):
socketserver.TCPServer.allow_reuse_address = True
server = socketserver.TCPServer((self.HOST, self.PORT), MyTCPHandler)
server.serve_forever()
def run(self):
print('run thread')
self.receive_packet()
class TestGUI(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.btn1 = QPushButton("start thread", self)
self.textbox1 = QLineEdit(self)
vertBox = QVBoxLayout()
vertBox.addWidget(self.btn1)
vertBox.addWidget(self.textbox1)
self.setLayout(vertBox)
self.setGeometry(700, 500, 300, 100)
self.btn1.clicked.connect(self.threadStart)
self.show()
self.th = TestThread(self)
#pyqtSlot()
def threadStart(self):
self.th.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
form = TestGUI()
app.exec_()
To send the information of a thread you can use QMetaObject.invokeMethod(), so it is necessary to pass the GUI, and in this case we take advantage that the parent of the QThread is the GUI.
You can set a new property to pass the GUI to the handler, and from the handler access that property through self.server.
Finally, we implemented a slot that receives the information.
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import socketserver
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
try:
data = self.rfile.read(28)
QMetaObject.invokeMethod(self.server.w, "setData",
Qt.QueuedConnection, Q_ARG(bytes, data))
except Exception as e:
print('MyTCPHandler.handle exception error: ', e)
class TestThread(QThread):
HOST, PORT = '192.168.0.102', 8000
def __init__(self, parent=None):
super().__init__(parent)
def receive_packet(self):
socketserver.TCPServer.allow_reuse_address = True
server = socketserver.TCPServer((self.HOST, self.PORT), MyTCPHandler)
server.w = self.parent()
server.serve_forever()
def run(self):
print('run thread')
self.receive_packet()
class TestGUI(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.btn1 = QPushButton("start thread", self)
self.textbox1 = QLineEdit(self)
vertBox = QVBoxLayout()
vertBox.addWidget(self.btn1)
vertBox.addWidget(self.textbox1)
self.setLayout(vertBox)
self.setGeometry(700, 500, 300, 100)
self.btn1.clicked.connect(self.threadStart)
self.show()
self.th = TestThread(self)
#pyqtSlot(bytes)
def setData(self, data):
print(data)
#pyqtSlot()
def threadStart(self):
self.th.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
form = TestGUI()
sys.exit(app.exec_())
#Plus: with signals
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import socketserver
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
try:
data = self.rfile.read(28)
self.server.qthread.dataChanged.emit(data)
except Exception as e:
print('MyTCPHandler.handle exception error: ', e)
class TestThread(QThread):
HOST, PORT = '192.168.0.102', 8000
dataChanged = pyqtSignal(bytes)
def __init__(self, parent=None):
super().__init__(parent)
def receive_packet(self):
socketserver.TCPServer.allow_reuse_address = True
server = socketserver.TCPServer((self.HOST, self.PORT), MyTCPHandler)
server.qthread = self
server.serve_forever()
def run(self):
print('run thread')
self.receive_packet()
class TestGUI(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.btn1 = QPushButton("start thread", self)
self.textbox1 = QLineEdit(self)
vertBox = QVBoxLayout()
vertBox.addWidget(self.btn1)
vertBox.addWidget(self.textbox1)
self.setLayout(vertBox)
self.setGeometry(700, 500, 300, 100)
self.btn1.clicked.connect(self.threadStart)
self.show()
self.th = TestThread(self)
self.th.dataChanged.connect(self.setData)
#pyqtSlot(bytes)
def setData(self, data):
print(data)
#pyqtSlot()
def threadStart(self):
self.th.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
form = TestGUI()
sys.exit(app.exec_())
I would like to ask how to make the text in QTextEdit scoll, to achieve an animational effect. The animational effect should be something like what in the video shows: https://www.youtube.com/watch?v=MyeuGdXv4XM
With PyQt I want to get this effect:
The text should be scolled automatically at a speed of 2 lines/second downwards, till it reaches the end and stops.
In my code below, when the button is clicked, the text is shown in QTextEdit-Widget. The text is very long, so that the scroll bar is shown.
My Problem:
I dont know how to make the animation effect. Thus I would like to ask your help to correct my code.
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
import time
list_longText = [" long text 1 - auto scrolling " * 1000, " long text 2 - auto scrolling " * 2000]
class Worker(QObject):
finished = pyqtSignal()
strTxt = pyqtSignal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
#pyqtSlot()
def onJob(self):
for i in range(2):
self.strTxt.emit(list_longText[i])
time.sleep(2)
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setFixedSize(600, 400)
self.setObjectName("window")
self.initUI()
def initUI(self):
self.txt = QTextEdit("", self)
self.btn = QPushButton("Button", self)
self.btn.clicked.connect(self.start)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.txt)
self.layout.addWidget(self.btn)
self.setLayout(self.layout)
self.show()
def start(self):
self.thread = QThread()
self.obj = Worker()
self.obj.strTxt.connect(self.showText)
self.obj.moveToThread(self.thread)
self.obj.finished.connect(self.thread.quit)
self.thread.started.connect(self.obj.onJob)
self.thread.start()
def showText(self, str):
self.txt.setText("{}".format(str))
self.autoScroll()
def autoScroll(self):
vsb = self.txt.verticalScrollBar()
if vsb.value() <= vsb.maximum():
vsb.setValue(vsb.value() + 2)
time.sleep(1)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
sys.exit(app.exec_())
Thanks very much for the help!
The task you want is not heavy, it is periodic so using a thread is inappropriate, for this task we can use QVariantAnimation.
The other part is to create a method that moves to a certain line of text for it we use QTextCursor next to findBlockByLineNumber() of QTextDocument.
And for the last one we must start moving to the last initial visible for it we use the cursorForPosition() method through the size of the viewport().
longText = "\n".join(["{}: long text - auto scrolling ".format(i) for i in range(100)])
class AnimationTextEdit(QTextEdit):
def __init__(self, *args, **kwargs):
QTextEdit.__init__(self, *args, **kwargs)
self.animation = QVariantAnimation(self)
self.animation.valueChanged.connect(self.move)
#pyqtSlot()
def startAnimation(self):
self.animation.stop()
lines_per_second = 2
self.moveToLine(0)
p = QPoint(self.viewport().width() - 1, self.viewport().height() - 1)
cursor = self.cursorForPosition(p)
self.animation.setStartValue(cursor.blockNumber())
self.animation.setEndValue(self.document().blockCount()-1)
self.animation.setDuration(self.animation.endValue()*1000/lines_per_second)
self.animation.start()
#pyqtSlot(QVariant)
def move(self, i):
cursor = QTextCursor(self.document().findBlockByLineNumber(i))
self.setTextCursor(cursor)
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setFixedSize(600, 400)
self.txt = AnimationTextEdit(self)
self.btn = QPushButton("Start", self)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.txt)
self.layout.addWidget(self.btn)
self.txt.append(longText)
self.txt.move(0)
self.btn.clicked.connect(self.txt.startAnimation)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
Update:
if you want a continuous movement you must use verticalScrollBar():
longText = "\n".join(["{}: long text - auto scrolling ".format(i) for i in range(100)])
class AnimationTextEdit(QTextEdit):
def __init__(self, *args, **kwargs):
QTextEdit.__init__(self, *args, **kwargs)
self.animation = QVariantAnimation(self)
self.animation.valueChanged.connect(self.moveToLine)
#pyqtSlot()
def startAnimation(self):
self.animation.stop()
self.animation.setStartValue(0)
self.animation.setEndValue(self.verticalScrollBar().maximum())
self.animation.setDuration(self.animation.endValue()*4)
self.animation.start()
#pyqtSlot(QVariant)
def moveToLine(self, i):
self.verticalScrollBar().setValue(i)
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setFixedSize(600, 400)
self.txt = AnimationTextEdit(self)
self.btn = QPushButton("Start", self)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.txt)
self.layout.addWidget(self.btn)
self.txt.append(longText)
self.txt.moveToLine(0)
self.btn.clicked.connect(self.txt.startAnimation)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
Is there any way I can make certain text on QTextEdit permanent. Applications like cmd.exe where the current user directory is displayed and the rest of the screen is up for input. I tried inserting a QLabel but unable to do so, here's my code, I am currently taking user input through a separate line edit.
UPDATE I had a look at Ipython QtConsole where the line number is displayed constantly, how can I do that, I am looking in the source but if anyone who already knows it, please do tell. Here is the QtConsole for ipython notebook, I am trying to replicate this.
import os
import sys
import PyQt4
import PyQt4.QtCore
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 command and press Return"))
self.le = QLineEdit()
self.te = QTextEdit()
self.lbl = QLabel(str(os.getcwd())+"> ")
# layout
layout = QVBoxLayout(self)
layout.addWidget(label)
layout.addWidget(self.le)
layout.addWidget(self.te)
self.setLayout(layout)
# styling
self.te.setReadOnly(True)
# create connection
self.mytext = str(self.le.text())
self.connect(self.le, PyQt4.QtCore.SIGNAL("returnPressed(void)"),
self.display)
def display(self):
mytext = str(self.le.text())
self.te.append(self.lbl +str(os.popen(mytext).read()))
self.le.setText("")
if __name__ == "__main__":
main()
A simple solution is to create a class that inherits from QTextEdit and overwrite and add the necessary attributes as shown below:
class TextEdit(QTextEdit):
def __init__(self, *args, **kwargs):
QTextEdit.__init__(self, *args, **kwargs)
self.staticText = os.getcwd()
self.counter = 1
self.setReadOnly(True)
def append(self, text):
n_text = "{text} [{number}] > ".format(text=self.staticText, number=self.counter)
self.counter += 1
QTextEdit.append(self, n_text+text)
Complete Code:
import os
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class TextEdit(QTextEdit):
def __init__(self, *args, **kwargs):
QTextEdit.__init__(self, *args, **kwargs)
self.staticText = os.getcwd()
self.counter = 1
self.setReadOnly(True)
def append(self, text):
n_text = "{text} [{number}] > ".format(text=self.staticText, number=self.counter)
self.counter += 1
QTextEdit.append(self, n_text+text)
class MyWindow(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
label = QLabel(self.tr("Enter command and press Return"), self)
self.le = QLineEdit(self)
self.te = TextEdit(self)
# layout
layout = QVBoxLayout(self)
layout.addWidget(label)
layout.addWidget(self.le)
layout.addWidget(self.te)
self.setLayout(layout)
self.connect(self.le, SIGNAL("returnPressed(void)"), self.display)
# self.le.returnPressed.connect(self.display)
def display(self):
command = str(self.le.text())
resp = str(os.popen(command).read())
self.te.append(resp)
self.le.clear()
def main():
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
To emulate QtConsole we must overwrite some methods of QTextEdit, catch some events, and verify that it does not eliminate the prefix as I show below:
import os
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class TextEdit(QTextEdit):
def __init__(self, *args, **kwargs):
QTextEdit.__init__(self, *args, **kwargs)
self.staticText = os.getcwd()
self.counter = 1
self.prefix = ""
self.callPrefix()
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.onCustomContextMenuRequest)
def onCustomContextMenuRequest(self, point):
menu = self.createStandardContextMenu()
for action in menu.actions():
if "Delete" in action.text():
action.triggered.disconnect()
menu.removeAction(action)
elif "Cu&t" in action.text():
action.triggered.disconnect()
menu.removeAction(action)
elif "Paste" in action.text():
action.triggered.disconnect()
act = menu.exec_(point)
if act:
if "Paste" in act.text():
self.customPaste()
def customPaste(self):
self.moveCursor(QTextCursor.End)
self.insertPlainText(QApplication.clipboard().text())
self.moveCursor(QTextCursor.End)
def clearCurrentLine(self):
cs = self.textCursor()
cs.movePosition(QTextCursor.StartOfLine)
cs.movePosition(QTextCursor.EndOfLine)
cs.select(QTextCursor.LineUnderCursor)
text = cs.removeSelectedText()
def isPrefix(self, text):
return self.prefix == text
def getCurrentLine(self):
cs = self.textCursor()
cs.movePosition(QTextCursor.StartOfLine)
cs.movePosition(QTextCursor.EndOfLine)
cs.select(QTextCursor.LineUnderCursor)
text = cs.selectedText()
return text
def keyPressEvent(self, event):
if event.key() == Qt.Key_Return:
command = self.getCurrentLine()[len(self.prefix):]
self.execute(command)
self.callPrefix()
return
elif event.key() == Qt.Key_Backspace:
if self.prefix == self.getCurrentLine():
return
elif event.matches(QKeySequence.Delete):
return
if event.matches(QKeySequence.Paste):
self.customPaste()
return
elif self.textCursor().hasSelection():
t = self.toPlainText()
self.textCursor().clearSelection()
QTextEdit.keyPressEvent(self, event)
self.setPlainText(t)
self.moveCursor(QTextCursor.End)
return
QTextEdit.keyPressEvent(self, event)
def callPrefix(self):
self.prefix = "{text} [{number}] >".format(text=self.staticText, number=self.counter)
self.counter += 1
self.append(self.prefix)
def execute(self, command):
resp = os.popen(command).read()
self.append(resp)
class MyWindow(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
label = QLabel(self.tr("Enter command and press Return"), self)
self.te = TextEdit(self)
# layout
layout = QVBoxLayout(self)
layout.addWidget(label)
layout.addWidget(self.te)
self.setLayout(layout)
def main():
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()