I got the emit signal working when it is in the Worker class - def run(self) method. Everything runs fine, the while loop is able to cycle and emit a signal every 1 second. Then the label will be updated after receiving the signal.
I decided to try out by placing the while loop in another class named loop, methodA. This is to try see if the emitted signal will be picked up by the MainWindow. Unfortunately, the signal is not picked up and the program hung up thereafter.
Did I miss any step that prevented the while loop's emitting signals from being picked up? Kindly point me in the right direction by making changes to my script.
Thanks.
import sys
import time
from PyQt5 import QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal
from mydialog import Ui_mydialog
class Mainwindow(QtWidgets.QMainWindow, Ui_mydialog):
def __init__(self, *args, obj=None, **kwargs):
super(Mainwindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.thread = Worker()
self.loop = loop()
self.thread.sig.connect(self.updatelabel)
self.mypushbutton.clicked.connect(self.mypushbuttonclicked)
def mypushbuttonclicked(self):
self.thread.start()
def updatelabel(self, text):
self.mylabel.setText(text)
class Worker(QThread):
sig = pyqtSignal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
# self.count = 0
self.loop = loop()
def run(self):
self.loop.methodA()
## Original code without being in class loop and method loopA
# while True:
# time.sleep(1)
# self.count += 1
# if (self.count % 1 == 0):
# self.sig.emit(f"Timer: {self.count} s")
# Newly added class with method "methodA"
class loop(object):
sig = pyqtSignal(str)
def __init__(self):
self.count = 0
def methodA(self):
while True:
time.sleep(1)
self.count += 1
if (self.count % 1 == 0):
self.sig.emit(f"Timer: {self.count} s")
app = QtWidgets.QApplication(sys.argv)
window = Mainwindow()
app.setStyle("Fusion")
window.show()
app.exec()
I had a similar problem.
I solved it by following this: http://zetcode.com/gui/pyqt5/eventssignals/
The idea is to create a class that hold all the signals, and pass the same communication class to all the classes as a parameter.
So your code might become:
import sys
import time
from PyQt5 import QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal, QObject
from mydialog import Ui_mydialog
class Communicate(QObject):
sig = pyqtSignal(str)
class Mainwindow(QtWidgets.QMainWindow, Ui_mydialog):
def __init__(self, *args, obj=None, **kwargs):
super(Mainwindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.communicate = Communicate()
self.communicate.sig[str].connect(self.updatelabel)
self.thread = Worker(communicate = self.communicate)
#self.loop = loop() # this seems useless to me here
self.mypushbutton.clicked.connect(self.mypushbuttonclicked)
def mypushbuttonclicked(self):
self.thread.start()
def updatelabel(self, text):
self.mylabel.setText(text)
class Worker(QThread):
def __init__(self, parent=None, communicate=Communicate()):
super(Worker, self).__init__(parent)
self.communicate = communicate
# self.count = 0
self.loop = loop(communicate= self.communicate)
def run(self):
self.loop.methodA()
## Original code without being in class loop and method loopA
# while True:
# time.sleep(1)
# self.count += 1
# if (self.count % 1 == 0):
# self.sig.emit(f"Timer: {self.count} s")
# Newly added class with method "methodA"
class loop(object):
def __init__(self, communicate=Communicate()):
self.count = 0
self.communicate = communicate
def methodA(self):
while True:
time.sleep(1)
self.count += 1
if (self.count % 1 == 0):
self.communicate.sig.emit(f"Timer: {self.count} s")
app = QtWidgets.QApplication(sys.argv)
window = Mainwindow()
app.setStyle("Fusion")
window.show()
app.exec()
I haven't tested this code, but I hope that you've got the idea.
Related
I want to know if there is any workaround to the missing flow() property "BottomToTop"?
I'm currently working on a little pet project. Simply a QListWidget, containing a custom made QWidget item, added by the user. No problem with that part. I just want the item to be listed "BottomToTop". Any suggestions?
Do just need to insert the new elements at the bottom instead of the top of your list?
I assume what you call bottom in the beginning for your list?
If so, you can use QListWidget::insertItem
yourListWidget->(0, yourNewWidgetItem);
This is my closest solution to what I've been asking (Credit to -ymoreau for the Spacer idea).
But I'm not completely happy with it. When enough items are added and the scrollbar appears, an annoying (to my eye) white-space appears at the bottom. Any solution to that? And ofc. is there a more elegant solution to this? Seems abit overkill to me...
from PyQt5.QtWidgets import (QApplication, QWidget, QListWidget,
QListWidgetItem, QVBoxLayout, QHBoxLayout,
QPushButton, QSpacerItem, QLabel, QSizePolicy)
from PyQt5.QtCore import QSize
from PyQt5 import QtCore
import sys
class SpacerWidget(QWidget):
def __init__(self, parent=None):
super(SpacerWidget, self).__init__(parent)
self.spacer = QSpacerItem(self.width(), self.height(),
QSizePolicy.Expanding,
QSizePolicy.Expanding)
self.setStyleSheet("border: none;")
self.layout = QHBoxLayout()
self.layout.setSpacing(0)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.addItem(self.spacer)
self.setLayout(self.layout)
class SpacerWidgetItem(QListWidgetItem):
def __init__(self, parent=None):
super(SpacerWidgetItem, self).__init__(parent)
def setSize(self, width, new_height):
if new_height < 10:
self.setSizeHint(QSize(0, 0))
else:
self.setSizeHint(QSize(width, new_height))
class ItemWidget(QWidget):
def __init__(self, parent=None):
super(ItemWidget, self).__init__(parent)
self.left_label = QLabel("Some info!")
self.left_label.setMaximumHeight(35)
self.left_label.setAlignment(QtCore.Qt.AlignLeft)
self.right_label = QLabel("Some other info!")
self.right_label.setMaximumHeight(35)
self.right_label.setAlignment(QtCore.Qt.AlignRight)
self.layout = QHBoxLayout()
self.layout.setSpacing(0)
self.layout.addWidget(self.left_label)
self.layout.addWidget(self.right_label)
self.setLayout(self.layout)
class Widget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.resize(300, 320)
self.itemsinlist = 0
self.layout = QVBoxLayout()
self.add_btn = QPushButton("Add Item")
self.add_btn.setMinimumSize(QSize(300, 50))
self.add_btn.clicked.connect(self.addListItem)
self.list_w = QListWidget()
self.list_w.setMinimumSize(300, 270)
self.list_w.resize(300, 270)
self.list_w.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.list_w.setResizeMode(1)
self.spacer = SpacerWidgetItem(self.list_w)
self.spacer.setSizeHint(QSize(self.list_w.minimumWidth(),
self.list_w.minimumHeight()))
self.spacer_widget = SpacerWidget()
self.list_w.addItem(self.spacer)
self.list_w.setItemWidget(self.spacer, self.spacer_widget)
self.layout.addWidget(self.list_w)
self.layout.addWidget(self.add_btn)
self.layout.setContentsMargins(1, 1, 1, 1)
self.layout.setSpacing(0)
self.setLayout(self.layout)
self.show()
def addListItem(self):
new_label = QListWidgetItem(self.list_w)
new_label.setSizeHint(QSize(self.list_w.minimumWidth(), 35))
new_label_widget = ItemWidget()
self.list_w.addItem(new_label)
self.list_w.setItemWidget(new_label, new_label_widget)
self.list_w.scrollToBottom()
self.itemsinlist += 1
self.spacer.setSize(self.width(),
self.list_w.height() -
(self.itemsinlist * 35)-2)
def resizeEvent(self, event):
self.spacer.setSizeHint(QSize(self.width(),
self.list_w.height() -
(self.itemsinlist * 35)-2))
self.list_w.scrollToBottom()
def main():
app = QApplication(sys.argv)
wid = Widget()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
After reading about finite state machines I found the the QState/QStateMachine API in QT.
But now I can't find out how to use it to run different code based on which state I'm currently in.
Let's take the following example:
import sys
from PySide2 import QtCore, QtWidgets
class Form(QtWidgets.QDialog):
def action_a(self):
print("I'm in mode A")
def action_b(self):
print("Mode B is the current mode")
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.button1 = QtWidgets.QPushButton("Run action")
self.button2 = QtWidgets.QPushButton("Change State")
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.button1)
self.layout.addWidget(self.button2)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
form = Form()
form.show()
state_a = QtCore.QState()
state_b = QtCore.QState()
state_a.assignProperty(form.button2, "text", "To state B")
state_b.assignProperty(form.button2, "text", "To state A")
state_a.addTransition(form.button2, QtCore.SIGNAL("clicked()"), state_b)
state_b.addTransition(form.button2, QtCore.SIGNAL("clicked()"), state_a)
machine = QtCore.QStateMachine()
machine.addState(state_a)
machine.addState(state_b)
machine.setInitialState(state_a)
machine.start()
sys.exit(app.exec_())
What would I have to add to this code so that button1.clicked connects to action_a when in state_a, but to action_b when in state_b?
I have a folder TreeView on the left layout and I want to drag a .txt file from there and drop it to the other layout. Hopefully, I want to load the data of that dropped file on a variable.
For the code’s needs, I used till now (to my “real” code) the np.loadtxt() to load the data, so I’d like to use it here too.
In case it matters, the .txt file contains 4 columns (coordinates).
I post my code. The program closes when I drop the file.
Thanks in advance!
import sys, time, os
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import numpy as np
import pylab as pl
import random
class Example(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.folderLayout = QWidget();
self.pathRoot = QDir.rootPath()
self.dirmodel = QFileSystemModel(self)
self.dirmodel.setRootPath(QDir.currentPath())
self.indexRoot = self.dirmodel.index(self.dirmodel.rootPath())
self.folder_view = QTreeView();
self.folder_view.setDragEnabled(True)
self.folder_view.setModel(self.dirmodel)
self.folder_view.setRootIndex(self.indexRoot)
self.selectionModel = self.folder_view.selectionModel()
self.left_layout = QVBoxLayout()
self.left_layout.addWidget(self.folder_view)
self.folderLayout.setLayout(self.left_layout)
splitter_filebrowser = QSplitter(Qt.Horizontal)
splitter_filebrowser.addWidget(self.folderLayout)
splitter_filebrowser.addWidget(Figure_Canvas(self))
splitter_filebrowser.setStretchFactor(1, 1)
hbox = QHBoxLayout(self)
hbox.addWidget(splitter_filebrowser)
self.centralWidget().setLayout(hbox)
self.setWindowTitle('Simple drag & drop')
self.setGeometry(750, 100, 600, 500)
class Figure_Canvas(QWidget):
def __init__(self, parent):
super().__init__(parent)
self.setAcceptDrops(True)
blabla = QLineEdit()
self.right_layout = QVBoxLayout()
self.right_layout.addWidget(blabla)
self.buttonLayout = QWidget()
self.buttonLayout.setLayout(self.right_layout)
def dragEnterEvent(self, e):
if e.mimeData().hasFormat('text/uri-list'):
e.accept()
else:
e.ignore()
def dropEvent(self, e):
print("something")
data = np.loadtxt(e.mimeData())
print(data)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
app.exec_()
from PyQt4 import QtGui, QtCore
from selenium import webdriver
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.button = QtGui.QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
self.lbl = QtGui.QLabel(self)
menu1=QtGui.QComboBox(self)
for c in [" ","snak","python"]:
menu1.addItem(c)
menu1.activated[str].connect(self.b)
def b(self,a):
print (a) #it gives me the value selected in the menu option but the problem is i want to use the same value of "a" in another function
def handleButton(self):
driver = webdriver.Firefox()
driver.get('http://google.com')
driver.find_element_by_id("lst-ib").send_keys("a") # value of a should be same as value printed in function b
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I was trying embed matplotlib in python into Qt Designer as a custom widget, i followed one of those instruction online, i promote the widget to mplwidget.py and i coded file as code following
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
class MplCanvas(FigureCanvas):
def __init__(self):
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
FigureCanvas.__init__(self, self.fig)
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
class MplWidget(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.canvas = MplCanvas()
self.vbl = QtGui.QVBoxLayout()
self.vbl.addWidget(self.canvas)
self.setLayout(self.vbl)
I gives me the error
Traceback (most recent call last):
File "C:\Users\l.cen\Documents\Guiexmaple\main.py", line 8, in <module>
from window import Ui_MainWindow
File "C:\Users\l.cen\Documents\Guiexmaple\window.py", line 106, in <module>
from mplwidget import MplWidget
File "C:\Users\l.cen\Documents\Guiexmaple\mplwidget.py", line 9, in <module>
class MplCanvas(FigureCanvas):
File "C:\Users\l.cen\Documents\Guiexmaple\mplwidget.py", line 22, in MplCanvas
FigureCanvas.updateGeometry(self)
NameError: name 'self' is not defined
so i delete the setSizePolicy and updateGeometry part
then it will give
Traceback (most recent call last):
File "C:\Users\l.cen\Documents\Guiexmaple\main.py", line 83, in <module>
window= Main()
File "C:\Users\l.cen\Documents\Guiexmaple\main.py", line 74, in __init__
self.ui.setupUi(self)
File "C:\Users\l.cen\Documents\Guiexmaple\window.py", line 34, in setupUi
self.widget = MplWidget(self.centralwidget)
TypeError: __init__() takes exactly 1 argument (2 given)
I'm not sure what happened exactly since i followed all the steps in the instruction, anything suggestions that could relate to this would be great.
if it helps, this is code for my main body:
import sys
from PyQt4 import QtGui, QtCore
from window import Ui_MainWindow
import sqlite3
import os
import matplotlib.pyplot as plt
from datetime import datetime
import calendar
import numpy
os.chdir("C:\Data")
conn = sqlite3.connect('FBG.db')
c=conn.cursor()
class Main(QtGui.QMainWindow):
def searching_database(self):
self.ui.listWidget.clear()
data = self.ui.Inputname.text()
for df in c.execute("select name from sqlite_master where type='table'; "):
strdf=str(df)
if len(data)==0:
break
if strdf[3:(len(data)+3)] == data: # the name for df start from position 3 due to "[u "
self.ui.listWidget.addItem(strdf[3:-3])
else:
pass
def delete_selection(self):
self.ui.listWidget_3.takeItem(self.ui.listWidget_3.currentRow())
def clear_graph(self):
self.ui.listWidget_3.clear()
def adding_items(self):
global b
b=self.ui.listWidget.currentItem().text()
b=str(b)
def plot_graph(self):
self.ui.listWidget_3.addItem(b)
time1= QtCore.QDateTime(self.ui.dateTimeEdit.dateTime())
date1 = time1.toPyDateTime()
timestamp1 = calendar.timegm(date1.utctimetuple()) #return a integer value
time2= QtCore.QDateTime(self.ui.dateTimeEdit_2.dateTime())
date2 = time2.toPyDateTime()
timestamp2 = calendar.timegm(date2.utctimetuple())
time=[]
data=[]
for df in c.execute('''select * from '''+ b ):
time= numpy.append(time, df[0])
data= numpy.append(data, df[1])
plt.scatter(time,data,label= b)
plt.title("Time versus strain or temperture")
plt.xlabel("Time")
plt.ylabel("Strain or temperature")
plt.legend()
plt.show()
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.Inputname.textChanged.connect(self.searching_database)
self.ui.listWidget.itemClicked.connect(self.adding_items)
self.ui.pushButton.clicked.connect(self.plot_graph)
self.ui.Delete.clicked.connect(self.delete_selection)
self.ui.Clear.clicked.connect(self.clear_graph)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window= Main()
window.show()
app.exec_()
Here is a piece of code that might help you
#!/usr/bin/python
import sys
from PyQt4.QtGui import QWidget, QPushButton, QMainWindow, QMdiArea, QVBoxLayout, QApplication
from PyQt4.QtCore import Qt
from pylab import *
from matplotlib.backends.backend_qt4agg import (
FigureCanvasQTAgg as FigureCanvas,
NavigationToolbar2QTAgg as NavigationToolbar)
class MyMainWindow(QMainWindow):
def __init__(self, parent=None):
"""
"""
super(MyMainWindow,self).__init__(parent)
self.setWidgets()
def setWidgets(self, ):
vBox = QVBoxLayout()
mainFrame = QWidget()
self._plotGraphButton = QPushButton("Plot Random Graph")
self._plotGraphButton.clicked.connect(self.plotRandom)
self._fig = figure(facecolor="white")
self._ax = self._fig.add_subplot(111)
self._canvas = FigureCanvas(self._fig)
self._canvas.setParent(mainFrame)
self._canvas.setFocusPolicy(Qt.StrongFocus)
vBox.addWidget(self._plotGraphButton)
vBox.addWidget(self._canvas)
vBox.addWidget(NavigationToolbar(self._canvas,mainFrame))
mainFrame.setLayout(vBox)
self.setCentralWidget(mainFrame)
def plotRandom(self, ):
"""
"""
x = linspace(0,4*pi,1000)
self._ax.plot(x,sin(2*pi*rand()*2*x),lw=2)
self._canvas.draw()
if __name__ == '__main__':
qApp = QApplication(sys.argv)
MainWindow = MyMainWindow()
MainWindow.show()
sys.exit(qApp.exec_())
Cheers