Strange behavior of QProgressBar with PyQt4 - python

I have this sample of code:
import sys
import time
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Bar(QDialog):
def __init__(self, parent=None):
super(Bar, self).__init__()
self.pbar = QProgressBar(self)
self.pbar.setValue(0)
layout = QHBoxLayout()
layout.addWidget(self.pbar)
self.setLayout(layout)
def main(self):
for value in range(1, 100):
time.sleep(1)
print value
self.pbar.setValue(value)
app = QApplication(sys.argv)
form = Bar()
form.show()
form.main()
app.exec_()
I expect progressbar's value to increased by 1 every second.
Instead, although that all values printed on the screen, the progressbar shows only some of them. Also, the bar appears just when value == 5. I know how to achieve the appropriate result with QBasicTimer(), but why this one does not work? Did i make a stupid mistake?

Try adding a
QApplication.processEvents()
just after print value (this should force the UI to update).

Related

Qaction Shortcut in extended contextmenu not triggered

I try to extend the contextmenu of a QLineEdit with an additional entry for replacing text. I can extend the contextmenu with .createStandardContextMenu(), which works fine. But when I try to add a shortcut with .setShortcut(QKeySequence(Qt.CTRL + Qt.Key_R)) it will not react on the key. Same with different keys, which I tried. In addition the shortcut made with QAction('&Replace', self) doesn't work too.
Some examples here in SO and other sources are constructed in the same way, so I'm wondering that nobody else has got the same problem. Seems that I'm missing anything. But what? I can't figure out, checked the docs multiple times.
Working Example:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
class ECM(QWidget):
def __init__(self):
super(ECM, self).__init__()
self.setWindowTitle("Extended Context Menu")
self.lineEdit = QLineEdit()
self.lineEdit.setContextMenuPolicy(Qt.CustomContextMenu)
self.lineEdit.customContextMenuRequested.connect(self.my_contextMenuEvent)
layout = QVBoxLayout()
layout.addWidget(self.lineEdit)
self.setLayout(layout)
self.setFixedSize(800,200)
self.show()
def replace(self):
print("replace")
def my_contextMenuEvent(self):
print("my_contextMenuEvent")
menu = self.lineEdit.createStandardContextMenu()
action = QAction('&Replace', self)
action.setStatusTip('Replace values')
action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_R))
action.triggered.connect(self.replace)
menu.addAction(action)
menu.exec_()
if __name__ == '__main__':
app = QApplication(sys.argv)
sender = ECM()
app.exec_()
Based on musicamante's comment I came to the following solution:
Extract from the docs:
If you want to extend the standard context menu, reimplement this
function, call createStandardContextMenu() and extend the menu
returned.
The default use of the QAction list (as returned by actions()) is to
create a context QMenu.
It's not totally logically to me, not for the 1st time ;-)
Final code:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
class ECM(QWidget):
def __init__(self):
super(ECM, self).__init__()
self.setWindowTitle("Extended Context Menu")
self.lineEdit = QLineEdit()
self.lineEdit.setContextMenuPolicy(Qt.CustomContextMenu)
self.lineEdit.customContextMenuRequested.connect(self.my_contextMenuEvent)
layout = QVBoxLayout()
layout.addWidget(self.lineEdit)
self.setLayout(layout)
self.setFixedSize(800,200)
action = QAction('&Replace', self)
action.setStatusTip('Replace values')
action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_R))
action.triggered.connect(self.replace)
self.lineEdit.addAction(action)
self.show()
def replace(self):
print("replace")
def my_contextMenuEvent(self):
menu = self.lineEdit.createStandardContextMenu()
menu.addActions(self.lineEdit.actions())
menu.exec_()
if __name__ == '__main__':
app = QApplication(sys.argv)
sender = ECM()
app.exec_()

How to get the current Value of ProgressBar in pyQt5?

I'm Learning Pyqt5.
The problem is that i want to get the value of the progressbar in pyqt5
i tried using self.progressBar.getValue() or self.progressBar.getInt() None of them worked
The actual code is a little big.but never mind. Please help
i just need the syntax for getting the current Value from the progressbar ie: between 1 to 100
According to their documentation, the method for getting the value is just value(), so in your case, it would be self.progressBar.value()
I agree with #dustin-we, here is a minimal code example:
import sys
import time
from PyQt5.QtWidgets import (QApplication, QDialog,
QProgressBar, QPushButton)
TIME_LIMIT = 100
class Actions(QDialog):
"""
Simple dialog that consists of a Progress Bar and a Button.
Clicking on the button results in the start of a timer and
updates the progress bar.
"""
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('Progress Bar')
self.progress = QProgressBar(self)
self.progress.setGeometry(0, 0, 300, 25)
self.progress.setMaximum(100)
self.button = QPushButton('Start', self)
self.button.move(0, 30)
self.show()
self.button.clicked.connect(self.onButtonClick)
def onButtonClick(self):
count = 0
while count < TIME_LIMIT:
count += 1
time.sleep(0.01)
self.progress.setValue(count)
# !! Here is the command you need !!
print(self.progress.value())
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Actions()
sys.exit(app.exec_())

Why does my QTableWidgetItems's rowHeight not get properly set on refresh?

I'm creating a table widget that I want to auto-refresh at certain intervals. The trouble I'm having is that refreshing the contents of the table is resetting their rowHeight property and ignoring the call to setRowHeight().
For example, I have a repeater class here running on a separate thread:
class RepeatedTimer(QtCore.QThread):
def __init__(self, obj):
super(RepeatedTimer, self).__init__(obj)
self.obj = obj
self.stop = False
def run(self):
while not self.stop:
time.sleep(2)
self.obj.refresh()
and it's being used in my QTableWidget like this:
from PySide import QtCore, QtGui
import sys, time
class TestTable(QtGui.QTableWidget):
def __init__(self, parent=None):
super(TestTable, self).__init__(parent)
self.setColumnCount(1)
self.thread = RepeatedTimer(self) # Create the auto-refresher thread
self.thread.start() # Start the thread
self.refresh()
def refresh(self):
print "Clearing table"
while self.rowCount():
self.removeRow(0)
for each in xrange(3):
self.insertRow(each)
text = str(time.time())
item = QtGui.QTableWidgetItem(text)
self.setItem(each, 0, item)
for row in xrange(self.rowCount()):
self.setRowHeight(row, 100) # This part is not behaving as expected
print 'Row %d height: %d' % (row, self.rowHeight(row))
def closeEvent(self, event):
print 'Stopping thread...'
self.thread.stop = True
self.thread.exit()
app = QtGui.QApplication(sys.argv)
test = TestTable()
test.show()
sys.exit(app.exec_())
If you run this, you'll see that each time the table refreshes, it clears all the contents, adds new items in each row, and sets all the row heights to 100. Except that last part. It is correctly looping through the rows because it prints each time. But for some reason it stops setting the row heights after the first loop.
Any ideas why this is happening?
It is not necessary to create a thread to update the QTableWidget, you could use a QTimer, on the other hand remove QTableWidgetItem and set them again is expensive so I recommend you update them.
import sys
import time
from PySide import QtCore, QtGui
class TestTable(QtGui.QTableWidget):
def __init__(self, parent=None):
super(TestTable, self).__init__(parent)
self.setRowCount(3)
self.setColumnCount(1)
for row in range(self.rowCount()):
for col in range(self.columnCount()):
self.setItem(row, col, QtGui.QTableWidgetItem(str(time.time())))
for each in range(self.rowCount()):
self.setRowHeight(each, 100)
self.setColumnWidth(each, 250)
timer_refresh = QtCore.QTimer(self)
timer_refresh.timeout.connect(self.refresh)
timer_refresh.start(2000)
def refresh(self):
for row in range(self.rowCount()):
for col in range(self.columnCount()):
self.item(row, col).setText(str(time.time()))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
test = TestTable()
test.show()
sys.exit(app.exec_())
Sorry, but you do not need a separate thread for this task.
Use the QTimer class provides repetitive and single-shot timers.
Here is an example, sorry for me PyQt5
import sys
import time
#from PySide import QtCore, QtGui
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class TestTable(QTableWidget):
def __init__(self, parent=None):
super(TestTable, self).__init__(parent)
self.setWindowTitle("QTableWidget setRowHeight ")
self.resize(530, 330);
self.setRowCount(3)
self.setColumnCount(1)
self.setHorizontalHeaderLabels(['time.time())',])
for each in range(3):
self.setRowHeight(each, 100)
self.setColumnWidth(each, 250)
self.my_qtimer = QTimer(self)
self.my_qtimer.timeout.connect(self.timerTick)
self.my_qtimer.start(1000) # msec
def timerTick(self):
for each in range(3):
item = QTableWidgetItem(str(time.time()))
self.setItem(0, each, item)
app = QApplication(sys.argv)
test = TestTable()
test.show()
sys.exit(app.exec_())

PyQt4: use a QTimer to continually update progress bars

I have a simple dialog with three progress bars that I want to continually update (displaying system resource usage). From reading around the docs, QTimer is the right way to fire a function every x milliseconds (which would update the progress bars). However, I am not able to get it to work and I don't quite know why. It seems relatively simple to connect up the timer timeout signal to an update function, but it never seems to fire.
Here's my code:
import sys
from PyQt4 import QtGui, QtCore
import psutil
class Tiny_System_Monitor(QtGui.QWidget):
def __init__(self):
super(Tiny_System_Monitor, self).__init__()
self.initUI()
def initUI(self):
mainLayout = QtGui.QHBoxLayout()
self.cpu_progressBar = QtGui.QProgressBar()
self.cpu_progressBar.setTextVisible(False)
self.cpu_progressBar.setOrientation(QtCore.Qt.Vertical)
mainLayout.addWidget(self.cpu_progressBar)
self.vm_progressBar = QtGui.QProgressBar()
self.vm_progressBar.setOrientation(QtCore.Qt.Vertical)
mainLayout.addWidget(self.vm_progressBar)
self.swap_progressBar = QtGui.QProgressBar()
self.swap_progressBar.setOrientation(QtCore.Qt.Vertical)
mainLayout.addWidget(self.swap_progressBar)
self.setLayout(mainLayout)
timer = QtCore.QTimer()
timer.timeout.connect(self.updateMeters)
timer.start(1000)
def updateMeters(self):
cpuPercent = psutil.cpu_percent()
vmPercent = getattr(psutil.virtual_memory(), "percent")
swapPercent = getattr(psutil.swap_memory(), "percent")
self.cpu_progressBar.setValue(cpuPercent)
self.vm_progressBar.setValue(vmPercent)
self.swap_progressBar.setValue(swapPercent)
print "updated meters"
def main():
app = QtGui.QApplication(sys.argv)
ex = Tiny_System_Monitor()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You must keep a reference to the timer object, otherwise it will be immediately garbage-collected when initUI returns:
class Tiny_System_Monitor(QtGui.QWidget):
...
def initUI(self):
...
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateMeters)
self.timer.start(1000)

PySide (or PyQt) signals and slots basics

Consider a simple example like this which links two sliders using signals and slots:
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class MyMainWindow(QWidget):
def __init__(self):
QWidget.__init__(self, None)
vbox = QVBoxLayout()
sone = QSlider(Qt.Horizontal)
vbox.addWidget(sone)
stwo = QSlider(Qt.Horizontal)
vbox.addWidget(stwo)
sone.valueChanged.connect(stwo.setValue)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyMainWindow()
w.show()
sys.exit(app.exec_())
How would you change this so that the second slider moves in the opposite direction as the first? Slider one would be initialized with these values:
sone.setRange(0,99)
sone.setValue(0)
And slider two would be initialized with these values:
stwo.setRange(0,99)
stwo.setValue(99)
And then the value of stwo would be 99 - sone.sliderPosition.
How would you implement the signal and slot to make this work? I would appreciate a working example that builds on the simple example above.
Your example is a bit broken, because you forgot to set the parent of the layout, and also to save the slider widgets as member attributes to be accessed later... But to answer your question, its really as simple as just pointing your connection to your own function:
class MyMainWindow(QWidget):
def __init__(self):
QWidget.__init__(self, None)
vbox = QVBoxLayout(self)
self.sone = QSlider(Qt.Horizontal)
self.sone.setRange(0,99)
self.sone.setValue(0)
vbox.addWidget(self.sone)
self.stwo = QSlider(Qt.Horizontal)
self.stwo.setRange(0,99)
self.stwo.setValue(99)
vbox.addWidget(self.stwo)
self.sone.valueChanged.connect(self.sliderChanged)
def sliderChanged(self, val):
self.stwo.setValue(self.stwo.maximum() - val)
Note how sliderChanged() has the same signature as the original setValue() slot. Instead of connecting one widget directly to the other, you connect it to a custom method and then transform the value to what you want, and act how you want (setting a custom value on stwo)
You can connect signals to functions that do things. Your code isn't structured to do that easily and required refactoring, so you can do it the easy way:
stwo.setInvertedAppearance(True)
sone.valueChanged.connect(stwo.setValue)
Here's the way I did it. I added this class which reimplements setValue. (I got the idea from http://zetcode.com/tutorials/pyqt4/eventsandsignals/)
class MySlider(QSlider):
def __init__(self):
QSlider.__init__(self, Qt.Horizontal)
def setValue(self, int):
QSlider.setValue(self, 99-int)
Here's the complete code. Is this a good approach?
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class MySlider(QSlider):
def __init__(self):
QSlider.__init__(self, Qt.Horizontal)
def setValue(self, int):
QSlider.setValue(self, 99-int)
class MyMainWindow(QWidget):
def __init__(self):
QWidget.__init__(self, None)
vbox = QVBoxLayout()
sone = QSlider(Qt.Horizontal)
sone.setRange(0,99)
sone.setValue(0)
vbox.addWidget(sone)
stwo = MySlider()
stwo.setRange(0,99)
stwo.setValue(0)
vbox.addWidget(stwo)
sone.valueChanged.connect(stwo.setValue)
self.setLayout(vbox)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyMainWindow()
w.show()
sys.exit(app.exec_())

Categories