How to connect two classes using PyQt Signal Slot? - python

i'm writing an application in python using twisted and PyQt. What i'm trying to do is that when a client registers , it is reflected in the front end. So when the server registers the client it emit's an signal which will be trapped and a slot will be triggered but , it doesn't seem to work. Both of the signal slot in the code are not functioning.
from PyQt4 import QtGui, QtCore
import sys
from twisted.internet.protocol import Factory, Protocol
from twisted.protocols import amp
import qt4reactor
class register_procedure(amp.Command):
arguments = [('MAC',amp.String()),
('IP',amp.String()),
('Computer_Name',amp.String()),
('OS',amp.String())
]
response = [('req_status', amp.String()),
('ALIGN_FUNCTION', amp.String()),
('ALIGN_Confirmation', amp.Integer()),
('Callback_offset',amp.Integer())
]
class Ui_MainWindow(QtGui.QMainWindow):
def __init__(self,reactor, parent=None):
super(Ui_MainWindow,self).__init__(parent)
self.reactor=reactor
self.pf = Factory()
self.pf.protocol = Protocol
self.reactor.listenTCP(3610, self.pf) # listen on port 1234
def setupUi(self,MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(903, 677)
self.centralwidget = QtGui.QWidget(MainWindow)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
self.centralwidget.setSizePolicy(sizePolicy)
self.create_item()
self.retranslateUi(MainWindow)
self.connect(self, QtCore.SIGNAL('triggered()'), self.closeEvent)
QtCore.QObject.connect(self,QtCore.SIGNAL('registered()'),self.registered)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
self.pushButton_4.setText(_translate("MainWindow", "Delete System ", None))
self.pushButton.setText(_translate("MainWindow", "Add System", None))
self.label_2.setText(_translate("MainWindow", "SYSTEM STATUS", None))
self.label.setText(_translate("MainWindow", "Monitoring Output", None))
def registered(self):# this function is not being triggered
print "check"
self.textbrowser.append()
def closeEvent(self, event):#neither is this being triggered
print "asdf"
self.rector.stop()
MainWindow.close()
event.accept()
class bridge(QtCore.QObject):
mySignal = QtCore.SIGNAL('mySignal(QString)')
def __init__(self):
QtCore.QObject.__init__(self)
def registered(self):
print "p op"
QtCore.QObject.emit(self, QtCore.SIGNAL('registered'))
class Protocol(amp.AMP):
#register_procedure.responder
def register_procedure(self,MAC,IP,Computer_Name,OS):
self.bridge_conn=bridge()
cursor_device.execute("""select * FROM devices where MAC = ?;""",[(MAC)])
exists_val=cursor_device.fetchone()
cursor_device.fetchone()
print "register"
if not exists_val== "":
cursor_device.execute("""update devices set IP= ? , Computer_name= ? , OS = ? where MAC= ?;""",[IP,Computer_Name,OS,MAC])
self.bridge_conn.registered() # <--emits signal
return {'req_status': "done" ,'ALIGN_FUNCTION':'none','ALIGN_Confirmation':0,'Callback_offset':call_offset(1)}
else:
cursor_device.execute("""INSERT INTO devices(Mac,Ip,Computer_name,Os) values (?,?,?,?);""",[MAC,IP,Computer_Name,OS])
self.bridge_conn.registered()#<--emits signal
return {'req_status': "done" ,'ALIGN_FUNCTION':'main_loop()','ALIGN_Confirmation':0,'Callback_offset':0}
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
try:
import qt4reactor
except ImportError:
from twisted.internet import qt4reactor
qt4reactor.install()
from twisted.internet import reactor
MainWindow = QtGui.QMainWindow() # <-- Instantiate QMainWindow object.
ui = Ui_MainWindow(reactor)
ui.setupUi(MainWindow)
MainWindow.show()
reactor.run()

here is how I connected two classes, I dont know is that the best way, but it works as I wanted:
class MyForm(QtGui.QMainWindow):
def __init__(self):
....
self.connect(self, QtCore.SIGNAL("mySignal(PyQt_PyObject)"), self.doSomething)
....
def someMethod(self):
...
button = MyPushButton(i, self)
...
def doSomething(self):
...
class MyPushButton(QtGui.QPushButton):
def __init__(self, elementID, mForm):
super(MyPushButton, self).__init__()
self.__elementID = elementID
self.__mForm = mForm
self.connect(self, QtCore.SIGNAL('clicked()'), self, QtCore.SLOT("triggerOutput()"))
#QtCore.pyqtSlot()
def triggerOutput(self):
self.__mForm.emit(QtCore.SIGNAL("mySignal(PyQt_PyObject)"), self.__elementID)
so, important is last row, where I've emitted signal from MyPushButton class (self) to MyForm class (__mForm) , and doSomething() is called. But as you can see in this line: button = MyPushButton(i, self), I have passed self (MyForm) to MyPushButton.
I hope this will help you somehow.

Related

Python threading - Change variable value in another class

I am looking for a way to change the variables between classes in different threads.
As I had a question about threading recently, I'd like to use that example code for this question again.
Main-File:
#PROGRAM/SCRIPT
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication
import sys
import GUI
import datetime
import pyqtgraph
import time
plotsize = 20
class Worker2(QtCore.QThread):
def __init__(self):
print("Thread2 has been started")
def run(self):
#now get access to variable of class Worker
# =============================================================================
# Threading for not freezing the GUI while running
# =============================================================================
class Worker(QtCore.QThread):
progress = QtCore.pyqtSignal(int)
finished = QtCore.pyqtSignal()
widgetplot = QtCore.pyqtSignal(list, list)
def __init__(self, plot_time, plot_value):
print("Thread has been started")
QtCore.QThread.__init__(self, objectName='WorkerThread')
self.plot_time = plot_time
self.plot_value = plot_value
def run(self):
#now get access to variable of Worker2
_count = 0
_start_time = datetime.datetime.now()
while 0 <= _count < 100:
# Use local variable!
_count_prev = _count
QtCore.QThread.usleep(10000)
_diff = datetime.datetime.now() - _start_time
_count = int((_diff.total_seconds() * 10))
if(_count != _count_prev):
print(_count)
x = self.plot_time[:_count]
y = self.plot_value[:_count]
self.widgetplot.emit(x, y)
class my_class(QtWidgets.QMainWindow, GUI.Ui_MainWindow):
def __init__(self, parent=None):
super(my_class, self).__init__(parent)
self.setupUi(self)
self.WidgetPlot.setXRange(0, 105, padding=0)
self.WidgetPlot.setYRange(0, 105, padding=0)
self.second_line = pyqtgraph.PlotDataItem(pen=pyqtgraph.mkPen('w', width=plotsize*2))
self.plot_time = []
self.plot_value = []
self.worker_thread = Worker(self.plot_time, self.plot_value)
self.worker_thread.widgetplot.connect(self.update_second_line_plot)
self.worker_thread2 = Worker2()
self.pushButton.clicked.connect(self.my_function)
self.WidgetPlot.setMouseEnabled(x=False, y=False)
font=QtGui.QFont()
font.setPixelSize(20)
font.setBold(True)
self.WidgetPlot.getAxis("bottom").setTickFont(font)
self.WidgetPlot.getAxis("left").setTickFont(font)
def my_function(self):
_l = list(range(100))
self.plot_time.extend(_l)
self.plot_value.extend(_l)
self.start()
def update_second_line_plot(self, plot_time, plot_value):
self.second_line.setData(plot_time, plot_value)
def start(self):
self.WidgetPlot.plot(self.plot_time, self.plot_value, pen=pyqtgraph.mkPen('r', width=plotsize))
self.WidgetPlot.addItem(self.second_line)
self.worker_thread.start()
self.worker_thread2.start()
def main():
app = QApplication(sys.argv)
form = my_class()
form.show()
app.exec_()
if __name__ == '__main__':
main()
GUI:
#GUI.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(739, 532)
MainWindow.setStyleSheet("")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.WidgetPlot = PlotWidget(self.centralwidget)
self.WidgetPlot.setGeometry(QtCore.QRect(100, 40, 541, 341))
self.WidgetPlot.setObjectName("WidgetPlot")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(330, 420, 93, 28))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Main Window"))
self.pushButton.setText(_translate("MainWindow", "Start"))
from pyqtgraph import PlotWidget
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
I'd like to change variable in class Worker2 from class Worker and vice versa.
Many thanks in advance!
Andrew
but for the other class there is no way to call/change variables...
Member variables* in Python are public by default.** Suppose you have
class P:
def __init__(self):
self.q = "whatever"
def printit(self):
print(self.q)
You can do this in the repl:
>>> p = P()
p = P()
>>> p.printit()
p.printit()
whatever
>>> p.q
p.q
'whatever'
>>> p.q = 'new value'
p.q = 'new value'
>>> p.printit()
p.printit()
new value
>>> p.q
p.q
'new value'
You can do the same from anywhere in Python code that has a reference to an object belonging to class P.
* "Attributes?" "fields?" I wrote a lot of Python programs that were ancillary to projects that I worked on—mostly for my own use. I haven't talked a lot with other Python programmers, and I don't really know the lingo.
** See https://towardsdatascience.com/private-protected-attributes-in-python-demystified-once-and-for-all-9456d4e56414 for more about "public" vs. "private."

Passing retrived serial data from worker class variable to main gui class

I have spent a few days trying to get a separate thread to read the incoming serial data as if I loop through a function in the GUI class with a timer the GUI gets unresponsive as it tries to receive the data. I have managed to print the incoming serial on the console even though it has been done using a while loop, which I do not like too much, and I plan to replace it with a timer.
Now I have troubles passing the variable decoded_serial from the WorkerThread class to the displaySerial function in the main GUI class.
This is the code I have been working on:
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import QThread, QTimer
import serial
class WorkerThread(QThread):
def run(self):
ser= serial.Serial(port='COM5', baudrate=9600)
while (True):
if (ser.inWaiting() > 0):
data_string = ser.read(ser.inWaiting()).decode('utf-8')
decoded_serial = (data_string)
print(decoded_serial)
class Ui_MainWindow(object):
def workerSignal(self):
self.worker.start()
print('Worker started')
def setupUi(self, MainWindow):
self.worker = WorkerThread()
MainWindow.setObjectName('MainWindow')
MainWindow.resize(250, 300)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName('centralwidget')
self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox.setGeometry(QtCore.QRect(30, 100, 201, 161))
self.groupBox.setObjectName('groupBox')
self.LineEdit = QtWidgets.QLineEdit(self.groupBox)
self.LineEdit.setGeometry(QtCore.QRect(10, 50, 181, 20))
self.LineEdit.setObjectName('LineEdit')
self.Label = QtWidgets.QLabel(self.groupBox)
self.Label.setGeometry(QtCore.QRect(10, 30, 181, 10))
self.Label.setObjectName('Label')
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate('MainWindow', 'TestApp'))
self.groupBox.setTitle(_translate('MainWindow', 'Serial'))
self.Label.setText(_translate('MainWindow', 'Serial'))
def displaySerial(self):
self.LineEdit.setText(decoded_serial)
if __name__ == '__main__':
import sys
def eventTimer():
ui.workerSignal()
def serialLoop():
ui.displaySerial()
app = QtWidgets.QApplication(sys.argv)
QtCore.QTimer.singleShot(0, eventTimer)
serial_timer = QtCore.QTimer()
serial_timer.timeout.connect(serialLoop)
serial_timer.start(1000)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec())
To try to pass the value to the main GUI class I have tried something like:
class WorkerThread(QThread):
def run(self):
WorkerThread.data = decoded_serial
class Ui_MainWindow(object, WorkerThread):
print(WorkerThread.data)
Although I do not seem to be doing it the right way, and it returns some errors.
What would be the best approach in order to send the retrieved serial string to the line edit field?
I have had a look at multiple solutions presented in the past, but I do not seem to understand how to implement those in my code.
Any guidance is highly appreciated!

QTextBrowser text broken (with QThread for python) - Solved [duplicate]

I have a GUI made in Designer (pyqt5). A function in my main class needs to work on a separate thread. I also catch the stdout on a QtextEdit LIVE during operations. Everything so far works.
Right now I'm trying to implement a ProgressBar onto my main GUI form. The bar needs to show live progression just like it does on the textEdit.
The example code below works on Linux without any warnings. But on Windows I get the error:
QObject::setParent: Cannot set parent, new parent is in a different thread
I know that this is due to me having a ui element modification within my threaded function. I did my research but all the answers point to using QThreads (just when I started to understand basic threading!). I would prefer a way to update my GUI without having to change the current threading system below.
Here is the example code:
import sys
import threading
import time
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtGui import QTextCursor
from ui_form import Ui_Form
class EmittingStream(QObject):
textWritten = pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
class Form(QMainWindow):
finished = pyqtSignal()
def __init__(self, parent=None):
super(Form, self).__init__(parent)
# Install the custom output stream
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
self.ui = Ui_Form()
self.ui.setupUi(self)
self.ui.pushButton_run.clicked.connect(self.start_task)
self.finished.connect(self.end_task)
def start_task(self):
self.thread = threading.Thread(target=self.run_test)
self.thread.start()
self.ui.pushButton_run.setEnabled(False)
def end_task(self):
self.ui.pushButton_run.setEnabled(True)
def __del__(self):
# Restore sys.stdout
sys.stdout = sys.__stdout__
def normalOutputWritten(self, text):
"""Append text to the QTextEdit."""
cursor = self.ui.textEdit.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.insertText(text)
self.ui.textEdit.setTextCursor(cursor)
self.ui.textEdit.ensureCursorVisible()
def run_test(self):
for i in range(100):
per = i + 1
self.ui.progressBar.setValue(per)
print("%%%s" % per)
time.sleep(0.15) # simulating expensive task
print("Task Completed!")
time.sleep(1.5)
self.ui.progressBar.reset()
self.finished.emit()
def main():
app = QApplication(sys.argv)
form = Form()
form.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
the ui:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'form.ui'
#
# Created: Mon Apr 30 13:43:19 2018
# by: PyQt5 UI code generator 5.2.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(Form)
self.centralwidget.setObjectName("centralwidget")
self.pushButton_run = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_run.setGeometry(QtCore.QRect(40, 20, 311, 191))
self.pushButton_run.setObjectName("pushButton_run")
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
self.textEdit.setGeometry(QtCore.QRect(40, 230, 721, 241))
self.textEdit.setObjectName("textEdit")
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(40, 490, 721, 23))
self.progressBar.setObjectName("progressBar")
Form.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(Form)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 25))
self.menubar.setObjectName("menubar")
Form.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(Form)
self.statusbar.setObjectName("statusbar")
Form.setStatusBar(self.statusbar)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "MainWindow"))
self.pushButton_run.setText(_translate("Form", "RUN"))
Somehow I need to -instantly- inform the gui thread (from my running thread) that the progress bar value is changing (a process that could take up minutes to complete).
Define a custom signal that sends updates to the progress-bar:
class Form(QMainWindow):
finished = pyqtSignal()
updateProgress = pyqtSignal(int)
def __init__(self, parent=None):
super(Form, self).__init__(parent)
...
self.updateProgress.connect(self.ui.progressBar.setValue)
def run_test(self):
for i in range(100):
per = i + 1
self.updateProgress.emit(per)
...

How to properly add OAuth2 Popup in existing PyQt5 Application

I'm pretty new to PyQt, and I've managed to fumble my way through putting together a working Application using Qt Designer. However, I need to add an OAuth2 flow to sign into a particular service, and I'm having trouble getting that to fit in my existing Application.
I found a great example that takes care of the PyQt5/OAuth2 login in Github (https://github.com/alonraiz/QT-OAuth-Example) - as long as I run it standalone. I'm now trying to integrate it so that it pops up when the user selects a specific menu item... and I'm having trouble.
The default code uses this:
app = QApplication(sys.argv)
browser = LoginWindow(app)
Since I already have an app, I've tried just calling:
browser = LoginWindow(app)
Using my existing app object. Which then gets me to this error:
QCoreApplication::exec: The event loop is already running
Which seems to make sense. So I delete this:
sys.exit(app.exec_())
But then I just get nothing.
Based on my limited PyQt5 experience, I'm sure I'm going about this completely wrong...
Here is an example illustrating where I'm at right now. Appreciate any help!
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'test.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!
import sys
from urllib.parse import urlencode, parse_qs
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication
"""
login anxious-elephant#example.com
password Frantic-Magpie-Tame-Cow-9
"""
ClientId = '0oamu4rr08hdGKd9u0h7'
RedirectUrl = 'www.oauth.com/playground/authorization-code.html'
RedirectScheme = 'https://'
Scopes = ['photo offline_access']
ResponseType = 'code'
Headers = {'client_id': ClientId, 'redirect_uri': RedirectScheme+RedirectUrl, 'response_type': ResponseType,
'scope': str.join(' ', Scopes), 'state': 'RT6TfGb4jEWbz7SI'}
AuthUrl = 'https://dev-396343.oktapreview.com/oauth2/default/v1/authorize?{headers}'.format(
headers=urlencode(Headers))
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(234, 167)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 234, 22))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.actionLogin = QtWidgets.QAction(MainWindow)
self.actionLogin.setObjectName("actionLogin")
self.menuFile.addAction(self.actionLogin)
self.menubar.addAction(self.menuFile.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.actionLogin.setText(_translate("MainWindow", "Login"))
class RequestInterceptor(QWebEngineUrlRequestInterceptor):
def __init__(self, app):
super(RequestInterceptor, self).__init__()
self.app = app
def interceptRequest(self, info):
if RedirectUrl == (info.requestUrl().host()+info.requestUrl().path()):
params = parse_qs(info.requestUrl().query())
if 'code' in params.keys():
print('OAuth code is {code}'.format(code=params['code']))
self.app.quit()
# return params['code']
class LoginWindow(QWebEngineView):
logged_in = QtCore.pyqtSignal(['QString'])
def __init__(self, app):
super(LoginWindow, self).__init__()
self.nam = self.page()
self.app = app
self.setUrl(QUrl(AuthUrl))
self.show()
self.loadFinished.connect(self._loadFinished)
interceptor = RequestInterceptor(app)
self.page().profile().setUrlRequestInterceptor(interceptor)
# This needs enabled to get the working example running:
# sys.exit(app.exec_())
def _loadFinished(self, result):
self.page().toHtml(self.callable)
def callable(self, data):
self.html = data
class MainMenu(Ui_MainWindow):
def __init__(self, dialog, mainapp):
global fn
Ui_MainWindow.__init__(self)
self.setupUi(dialog)
self.actionLogin.triggered.connect(self.login)
self.mainapp = mainapp
def login(self):
print("login")
browser = LoginWindow(self.mainapp)
if __name__ == "__main__":
# This doesn't work
app = QtWidgets.QApplication(sys.argv)
menu = QtWidgets.QMainWindow()
prog = MainMenu(menu, app)
menu.show()
sys.exit(app.exec_())
# This works:
# app = QApplication(sys.argv)
# browser = LoginWindow(app)
What I'm looking to do is get the OAuth flow work as a popup from the main application rather than a standalone application (or get both applications living together in peace and harmony).
I recommend you read the PyQt5(1) docs so you know how to use the class generated by Qt Designer. Considering the above, the solution is to inherit from QMainWindow and use the Qt Designer class as the interface class. The interceptor object is a local variable that also generates a problem, so that it does not happen, pass the window as parent.
Considering the above, the solution is:
from urllib.parse import urlencode, parse_qs
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineCore, QtWebEngineWidgets
"""
login anxious-elephant#example.com
password Frantic-Magpie-Tame-Cow-9
"""
ClientId = "0oamu4rr08hdGKd9u0h7"
RedirectUrl = "www.oauth.com/playground/authorization-code.html"
RedirectScheme = "https://"
Scopes = ["photo offline_access"]
ResponseType = "code"
Headers = {
"client_id": ClientId,
"redirect_uri": RedirectScheme + RedirectUrl,
"response_type": ResponseType,
"scope": str.join(" ", Scopes),
"state": "RT6TfGb4jEWbz7SI",
}
AuthUrl = "https://dev-396343.oktapreview.com/oauth2/default/v1/authorize?{headers}".format(
headers=urlencode(Headers)
)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(234, 167)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 234, 22))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.actionLogin = QtWidgets.QAction(MainWindow)
self.actionLogin.setObjectName("actionLogin")
self.menuFile.addAction(self.actionLogin)
self.menubar.addAction(self.menuFile.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.actionLogin.setText(_translate("MainWindow", "Login"))
class RequestInterceptor(QtWebEngineCore.QWebEngineUrlRequestInterceptor):
codeChanged = QtCore.pyqtSignal(str)
def interceptRequest(self, info):
if RedirectUrl == (info.requestUrl().host() + info.requestUrl().path()):
params = parse_qs(info.requestUrl().query())
if "code" in params.keys():
code = params["code"][0][0]
print("OAuth code is {code}".format(code=params["code"][0][0]))
self.codeChanged.emit(code)
class LoginWindow(QtWebEngineWidgets.QWebEngineView):
codeChanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(LoginWindow, self).__init__(parent)
self.setUrl(QtCore.QUrl(AuthUrl))
self.loadFinished.connect(self._loadFinished)
interceptor = RequestInterceptor(self)
self.page().profile().setUrlRequestInterceptor(interceptor)
self.show()
self.interceptor.codeChanged.connect(self.codeChanged)
#QtCore.pyqtSlot(bool)
def _loadFinished(self, result):
self.page().toHtml(self.callable)
def callable(self, data):
self.html = data
class MainMenu(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainMenu, self).__init__(parent)
self.setupUi(self)
self.actionLogin.triggered.connect(self.login)
lay = QtWidgets.QVBoxLayout(self.centralwidget)
self.code_label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
lay.addWidget(self.code_label)
#QtCore.pyqtSlot()
def login(self):
self.browser = LoginWindow()
self.browser.codeChanged.connect(self.onCodeChanged)
#QtCore.pyqtSlot(str)
def onCodeChanged(self, code):
self.code_label.setText(code)
# self.browser.close()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
menu = MainMenu()
menu.show()
sys.exit(app.exec_())
(1) Using the Generated Code

how to emit signal from a non PyQt class?

i'm programming an application in python using twisted and PyQt . the problem that i'm facing is that when a function in my twisted code is executed i have to print a line in the GUI, i'm trying to achieve this by emiting a signal (Non PyQt class). This does not seem to work, i have a doubt that the twisted event loop is screwing things for PyQt. Because the closeEvent signal is not being trapped by the program.
Here's the code snippet:
from PyQt4 import QtGui, QtCore
import sys
from twisted.internet.protocol import Factory, Protocol
from twisted.protocols import amp
import qt4reactor
class register_procedure(amp.Command):
arguments = [('MAC',amp.String()),
('IP',amp.String()),
('Computer_Name',amp.String()),
('OS',amp.String())
]
response = [('req_status', amp.String()),
('ALIGN_FUNCTION', amp.String()),
('ALIGN_Confirmation', amp.Integer()),
('Callback_offset',amp.Integer())
]
class Ui_MainWindow(QtGui.QMainWindow):
def __init__(self,reactor, parent=None):
super(Ui_MainWindow,self).__init__(parent)
self.reactor=reactor
self.pf = Factory()
self.pf.protocol = Protocol
self.reactor.listenTCP(3610, self.pf) # listen on port 1234
def setupUi(self,MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(903, 677)
self.centralwidget = QtGui.QWidget(MainWindow)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
self.centralwidget.setSizePolicy(sizePolicy)
self.create_item()
self.retranslateUi(MainWindow)
self.connect(self, QtCore.SIGNAL('triggered()'), self.closeEvent)
QtCore.QObject.connect(self,QtCore.SIGNAL('registered()'),self.registered)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
self.pushButton_4.setText(_translate("MainWindow", "Delete System ", None))
self.pushButton.setText(_translate("MainWindow", "Add System", None))
self.label_2.setText(_translate("MainWindow", "SYSTEM STATUS", None))
self.label.setText(_translate("MainWindow", "Monitoring Output", None))
def registered(self):# this function is not being triggered
print "check"
self.textbrowser.append()
def closeEvent(self, event):#neither is this being triggered
print "asdf"
self.rector.stop()
MainWindow.close()
event.accept()
class Protocol(amp.AMP):
#register_procedure.responder
def register_procedure(self,MAC,IP,Computer_Name,OS):
self.bridge_conn=bridge()
cursor_device.execute("""select * FROM devices where MAC = ?;""",[(MAC)])
exists_val=cursor_device.fetchone()
cursor_device.fetchone()
print "register"
if not exists_val== "":
cursor_device.execute("""update devices set IP= ? , Computer_name= ? , OS = ? where MAC= ?;""",[IP,Computer_Name,OS,MAC])
QtCore.QObject.emit( QtCore.SIGNAL('registered')) # <--emits signal
return {'req_status': "done" ,'ALIGN_FUNCTION':'none','ALIGN_Confirmation':0,'Callback_offset':call_offset(1)}
else:
cursor_device.execute("""INSERT INTO devices(Mac,Ip,Computer_name,Os) values (?,?,?,?);""",[MAC,IP,Computer_Name,OS])
QtCore.QObject.emit( QtCore.SIGNAL('registered'))#<--emits signal
return {'req_status': "done" ,'ALIGN_FUNCTION':'main_loop()','ALIGN_Confirmation':0,'Callback_offset':0}
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
try:
import qt4reactor
except ImportError:
from twisted.internet import qt4reactor
qt4reactor.install()
from twisted.internet import reactor
MainWindow = QtGui.QMainWindow() # <-- Instantiate QMainWindow object.
ui = Ui_MainWindow(reactor)
ui.setupUi(MainWindow)
MainWindow.show()
reactor.run()
This is what I use in my one code for sending signals from QGraphicsItems (because they are not derived from QObject and cannot send/receive signals by default). It's basically a simplified version of Radio-'s answer.
from PyQt4 import QtGui as QG
from PyQt4 import QtCore as QC
class SenderObject(QC.QObject):
something_happened = QC.pyqtSignal()
SenderObject is a tiny class derived from QObject where you can put all the signals you need to emit. In this case only one is defined.
class SnapROIItem(QG.QGraphicsRectItem):
def __init__(self, parent = None):
super(SnapROIItem, self).__init__(parent)
self.sender = SenderObject()
def do_something_and_emit(self):
...
self.sender.something_happened.emit()
In the non-QObject class you add a SenderObject as a sender variable. Anywhere where the non-QObject class is used you can connect the signal from the sender to anything you need.
class ROIManager(QC.QObject):
def add_snaproi(self, snaproi):
snaproi.sender.something_happened.connect(...)
UPDATE
The full code is this and should print out "Something happened...":
from PyQt4 import QtGui as QG
from PyQt4 import QtCore as QC
class SenderObject(QC.QObject):
something_happened = QC.pyqtSignal()
class SnapROIItem(QG.QGraphicsItem):
def __init__(self, parent = None):
super(SnapROIItem, self).__init__(parent)
self.sender = SenderObject()
def do_something_and_emit(self):
self.sender.something_happened.emit()
class ROIManager(QC.QObject):
def __init__(self, parent=None):
super(ROIManager,self).__init__(parent)
def add_snaproi(self, snaproi):
snaproi.sender.something_happened.connect(self.new_roi)
def new_roi(self):
print 'Something happened in ROI!'
if __name__=="__main__":)
roimanager = ROIManager()
snaproi = SnapROIItem()
roimanager.add_snaproi(snaproi)
snaproi.do_something_and_emit()
UPDATE 2
Instead of
QtCore.QObject.connect(self,QtCore.SIGNAL('registered()'),self.registered)
you should have:
protocol.sender.registered.connect(self.registered)
This means you also need to get hold of the protocol instance in self.pf (by the way, do you import Protocol and then define it yourself as well?)
In the Protocol class instead of
QtCore.QObject.emit( QtCore.SIGNAL('registered')
you need to, first, instantiate a SenderObject in the Protocol.
class Protocol(amp.AMP):
def __init__( self, *args, **kw ):
super(Protocol, self).__init__(*args, **kw)
self.sender = SenderObject()
and then, in register_procedure emit the signal through sender:
self.sender.registered.emit()
For all of this to work you'll have to have defined SenderObject as:
class SenderObject(QC.QObject):
registered = QC.pyqtSignal()
This is an old post, but it helped me. Here is my version. One item that is not a QObject signaling two other non QObject's to run their methods.
from PyQt4 import QtGui, QtCore
class Signal(object):
class Emitter(QtCore.QObject):
registered = QtCore.pyqtSignal()
def __init__(self):
super(Signal.Emitter, self).__init__()
def __init__(self):
self.emitter = Signal.Emitter()
def register(self):
self.emitter.registered.emit()
def connect(self, signal, slot):
signal.emitter.registered.connect(slot)
class item(object):
def __init__(self, name):
self.name = name
self.signal = Signal()
def something(self):
print self.name, ' says something'
>>> itemA = item('a')
>>> itemB = item('b')
>>> itemC = item('c')
>>> itemA.signal.connect(itemA.signal, itemB.something)
>>> itemA.signal.connect(itemA.signal, itemC.something)
>>> itemA.signal.register()
b says something
c says something
The two basic problems are that:
1) Something has to know the sender and receiver of the signals
Consider a more frequent case in Qt where you may have multiple buttons, each with a 'clicked' signal. Slots need to know which button was clicked, so catching a generic signal does not make much sense.
and 2) The signals have to originate from a QObject.
Having said that, I'm not sure what the canonical implementation is. Here is one way to do it, using the idea of a Bridge that you had in one of your earlier posts, and a special Emitter class inside of Protocol. Running this code will simply print 'Working it' when protocol.register() is called.
from PyQt4 import QtGui, QtCore
import sys
class Ui_MainWindow(QtGui.QMainWindow):
def __init__(self):
super(Ui_MainWindow, self).__init__()
def work(self):
print "Working it"
class Protocol(object):
class Emitter(QtCore.QObject):
registered = QtCore.pyqtSignal()
def __init__(self):
super(Protocol.Emitter, self).__init__()
def __init__(self):
self.emitter = Protocol.Emitter()
def register(self):
self.emitter.registered.emit()
class Bridge(QtCore.QObject):
def __init__(self, gui, protocol):
super(Bridge, self).__init__()
self.gui = gui
self.protocol = protocol
def bridge(self):
self.protocol.emitter.registered.connect(self.gui.work)
app = QtGui.QApplication(sys.argv)
gui = Ui_MainWindow()
protocol = Protocol()
bridge = Bridge(gui, protocol)
bridge.bridge()
#protocol.register() #uncomment to see 'Working it' printed to the console
I improved the solution of #Ryan Trowbridge a bit to deal with a generic type. I hoped to use Generic[T] but turned out too complicated.
class GenSignal:
def __init__(self,typ):
Emitter = type('Emitter', (QtCore.QObject,), {'signal': Signal(typ)})
self.emitter = Emitter()
def emit(self,*args,**kw):
self.emitter.signal.emit(*args,**kw)
def connect(self, slot):
self.emitter.signal.connect(slot)
To use, x=GenSignal(int) and continue as usual.

Categories

Resources