How to properly add OAuth2 Popup in existing PyQt5 Application - python

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

Related

xbbg pulling bdp data with multithreading

I am trying to develop a pyqt5 app where I can live stream stock prices from Bloomberg. I put some field names (eg. LAST_TRADE) in the ThreadClass (QThread) to pull price every second. I also have a function in the main class to pull some non-realtime data (eg. PX_YEST_CLOSE) from Bloomberg whenever a push button is clicked. I ran into this issue that when the push button clicked, it sometimes returns LAST_TRADE data instead of PX_YEST_CLOSE. I think this is because the API only allow one request at a time. Is there a better way to workaround it?
I've included a simple version of my code below just to illustrate. Thank you!
import sys, time
from xbbg import blp
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow
from ui_interface import Ui_MainWindow
ticker = ['AMD Equity', 'NVDA Equity', 'CHK Equity', 'ABNB Equity', 'AFRM Equity', 'U Equity']
live_fields = ['LAST_TRADE']
class MainWindow(QMainWindow, Ui_MainWindow):
"""Class for the Main window"""
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.thread={}
self.getStaticData()
self.connectSignalsSlots()
self.start_worker()
def getStaticData(self):
'''Pull static data from Bloomberg'''
self.staticData = blp.bdp(list(set(ticker)), flds=['CUR_MKT_CAP', 'PX_YEST_CLOSE'])
self.label_2.setText(str(self.staticData.iat[0,0]))
def start_worker(self):
self.thread[1] = ThreadClass(parent = None)
self.thread[1].start()
self.thread[1].any_signal.connect(self.liveFeed)
def liveFeed(self,liveData):
self.label.setText(str(liveData.iat[0,0]))
def updateData(self):
'''Update table if clicked'''
print(blp.bdp(['AAPL Equity'], flds=['CUR_MKT_CAP', 'PX_YEST_CLOSE']))
def connectSignalsSlots(self):
'''Signal-slots connections'''
self.pushButton.clicked.connect(self.updateData)
class ThreadClass(QtCore.QThread):
any_signal = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super(ThreadClass, self).__init__(parent)
self.is_running = True
def run(self):
print('Starting...')
while (True):
data=blp.bdp(ticker, flds=live_fields)
time.sleep(0.1)
self.any_signal.emit(data)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
control = MainWindow()
widget = QtWidgets.QStackedWidget()
widget.addWidget(control)
widget.show()
sys.exit(app.exec())
UI below:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(590, 582)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.pushButton = QPushButton(self.centralwidget)
self.pushButton.setObjectName(u"pushButton")
self.pushButton.setGeometry(QRect(360, 80, 75, 23))
self.label = QLabel(self.centralwidget)
self.label.setObjectName(u"label")
self.label.setGeometry(QRect(90, 70, 171, 41))
self.label_2 = QLabel(self.centralwidget)
self.label_2.setObjectName(u"label_2")
self.label_2.setGeometry(QRect(90, 140, 171, 41))
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 590, 21))
MainWindow.setMenuBar(self.menubar)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
self.pushButton.setText(QCoreApplication.translate("MainWindow", u"Update", None))
self.label.setText(QCoreApplication.translate("MainWindow", u"TextLabel", None))
self.label_2.setText(QCoreApplication.translate("MainWindow", u"TextLabel", None))

Info-Error Dialogue Management in Threading application in PyQt5

I'm new to GUI-programming and need help with a QThread application. I have a gui program that makes a query and lists the results of the query on the screen. I want an information dialog to open when the listing on the screen is finished. I wrote a function for this and when the operation is successful, it works without any problems. But I couldn't figure out how to show an error dialog if an invalid link entry occurs. Thank you for helping.
-I have commented out some of the methods I tried and failed.
import sys
import requests
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox
from bs4 import BeautifulSoup
from time import sleep
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(476, 391)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(0, 270, 471, 81))
self.pushButton.setObjectName("pushButton")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(0, 0, 471, 31))
self.lineEdit.setObjectName("lineEdit")
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
self.listWidget.setGeometry(QtCore.QRect(0, 50, 471, 192))
self.listWidget.setObjectName("listWidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 476, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.running = False
self.pushButton.clicked.connect(self.startBs4Worker)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "GET"))
def reveiceData(self, data):
# This method is the method that will
# process the data sent by the thread.
if data == None:
return
_translate = QtCore.QCoreApplication.translate
item = QtWidgets.QListWidgetItem()
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(11)
item.setFont(font)
item.setCheckState(QtCore.Qt.Checked)
item.setText(_translate("MainWindow", data))
self.listWidget.addItem(item)
def setThreadStatus(self):
self.running = not self.running
def startBs4Worker(self):
if self.running:
print("Thread is still running ...")
return
else:
self.thread = QThread()
# We send the URL address that the thread will process as a parameter.
self.worker = Bs4Worker(url=self.lineEdit.text())
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.worker.finished.connect(self.setThreadStatus)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.notifyProgress.connect(self.reveiceData)
self.worker.finished.connect(self.uyariBox) #---> With this place active, I couldn't figure out how to activate the Error message when wrong connection was entered.
self.running = True
self.thread.start()
def uyariBox(self):
self.msg = QMessageBox()
self.msg.thread()
self.msg.setWindowTitle("Bilgi !")
self.msg.setText("Tüm Sonuçlar Getirildi")
self.msg.setIcon(QMessageBox.Information)
self.msg.exec_()
class Bs4Worker(QThread):
notifyProgress = QtCore.pyqtSignal(str)
def __init__(self, url, parent=None):
QThread.__init__(self, parent)
self.url = url
def run(self):
try:
r = requests.get(self.url)
soup = BeautifulSoup(r.content)
linkler = soup.find_all("a")
for link in linkler:
baslik = link.get("title")
# Sending header information to master class
self.notifyProgress.emit(baslik)
self.finished.connect(ui.uyariBox) #---> When you activate this place, the warning appears instantly and disappears immediately.
#---> and there is no click anywhere in the app. (Since completion is not pressed in the warning message)
self.finished.emit()
# self.finished.connect(ui.uyariBox) #----> Warning does not appear when you activate this place.
except:
print("link error")
self.finished.emit()
# When I create a new QMessageBox to show the error message, the application crashes.
Uygulama = QApplication(sys.argv)
menu = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(menu)
menu.show()
sys.exit(Uygulama.exec_())
You can pass the operation result as string to the uyariBox method.
First you can create another signal in Bs4Worker:
class Bs4Worker(QThread):
result = QtCore.pyqtSignal(str)
Change the previous worker.finished connection to worker.result:
def startBs4Worker(self):
else:
self.worker.result.connect(self.uyariBox)
#Previous was self.worker.finished.connect(self.uyariBox)
Add variable to function:
def uyariBox(self,message):
self.msg = QMessageBox()
self.msg.thread()
self.msg.setWindowTitle("Bilgi !")
self.msg.setText(message)
self.msg.setIcon(QMessageBox.Information)
self.msg.exec_()
Then use signal to pass on the result of the operation:
def run(self):
try:
self.result.emit("Tüm Sonuçlar Getirildi")
except Exception as e:
self.result.emit(f"Error: {e}")
Full code:
import sys
import requests
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox
from bs4 import BeautifulSoup
from time import sleep
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(476, 391)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(0, 270, 471, 81))
self.pushButton.setObjectName("pushButton")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(0, 0, 471, 31))
self.lineEdit.setObjectName("lineEdit")
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
self.listWidget.setGeometry(QtCore.QRect(0, 50, 471, 192))
self.listWidget.setObjectName("listWidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 476, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.running = False
self.pushButton.clicked.connect(self.startBs4Worker)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "GET"))
def reveiceData(self, data):
# This method is the method that will
# process the data sent by the thread.
if data == None:
return
_translate = QtCore.QCoreApplication.translate
item = QtWidgets.QListWidgetItem()
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(11)
item.setFont(font)
item.setCheckState(QtCore.Qt.Checked)
item.setText(_translate("MainWindow", data))
self.listWidget.addItem(item)
def setThreadStatus(self):
self.running = not self.running
def startBs4Worker(self):
if self.running:
print("Thread is still running ...")
return
else:
self.thread = QThread()
# We send the URL address that the thread will process as a parameter.
self.worker = Bs4Worker(url=self.lineEdit.text())
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.worker.finished.connect(self.setThreadStatus)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.notifyProgress.connect(self.reveiceData)
self.worker.result.connect(
self.uyariBox) # ---> With this place active, I couldn't figure out how to activate the Error message when wrong connection was entered.
self.running = True
self.thread.start()
def uyariBox(self,message):
self.msg = QMessageBox()
self.msg.thread()
self.msg.setWindowTitle("Bilgi !")
self.msg.setText(message)
self.msg.setIcon(QMessageBox.Information)
self.msg.exec_()
class Bs4Worker(QThread):
result = QtCore.pyqtSignal(str)
notifyProgress = QtCore.pyqtSignal(str)
def __init__(self, url, parent=None):
QThread.__init__(self, parent)
self.url = url
def run(self):
try:
r = requests.get(self.url)
soup = BeautifulSoup(r.content)
linkler = soup.find_all("a")
for link in linkler:
baslik = link.get("title")
# Sending header information to master class
self.notifyProgress.emit(baslik)
self.finished.connect(
ui.uyariBox) # ---> When you activate this place, the warning appears instantly and disappears immediately.
# ---> and there is no click anywhere in the app. (Since completion is not pressed in the warning message)
self.finished.emit()
self.result.emit("Tüm Sonuçlar Getirildi")
# self.finished.connect(ui.uyariBox) #----> Warning does not appear when you activate this place.
except Exception as e:
print("link error")
self.finished.emit()
self.result.emit(f"Error: {e}")
# When I create a new QMessageBox to show the error message, the application crashes.
Uygulama = QApplication(sys.argv)
menu = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(menu)
menu.show()
sys.exit(Uygulama.exec_())

How to stop a thread using a button

We are creating an object detection project using Object detection API. We are train the program making pyqt5 GUI application. Trying to run the training part using thread. We want to stop the running thread using a push button. Here the code sample
class stopClass(QtCore.QThread):
def __init__(self, parent=None):
super(stopClass, self).__init__(parent)
def startTrain(self):
#changing directory
os.chdir(r"c://tensorflow_1//models//research//object_detection")
args3 = shlex.split('python train.py --logtostderr --train_dir=training/ --
pipeline_config_path=training/faster_rcnn_inception_v2_pets.config')
subprocess.run(args3, shell = True)
return
def run(self):
self.startTrain()
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(701, 495)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.Annotation = QtWidgets.QPushButton(self.centralwidget)
self.Annotation.setGeometry(QtCore.QRect(480, 10, 181, 41))
self.Annotation.setToolTip("")
self.Annotation.setObjectName("Annotation")
self.Start_train = QtWidgets.QPushButton(self.centralwidget)
self.Start_train.setGeometry(QtCore.QRect(480, 110, 181, 41))
self.Start_train.setObjectName("Start_train")
self.Stop_train = QtWidgets.QPushButton(self.centralwidget)
self.Stop_train.setEnabled(False)
self.Stop_train.setGeometry(QtCore.QRect(480, 160, 181, 41))
self.Stop_train.setObjectName("Stop_train")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 701, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.Start_train.clicked.connect(self.starting)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.Start_train.setText(_translate("MainWindow", "start train"))
self.Stop_train.setText(_translate("MainWindow", "stop train"))
def starting(self):
self.stopThread = stopClass()
self.stopThread.start()
self.Stop_train.setEnabled(True)
self.Stop_train.clicked.connect(self.stopThread.exit)
self.Start_train.setEnabled(False)
In this case I do not see the need to use threads since it is enough to use QProcess that allows a simple handling of the execution of the script. On the other hand, do not modify the script generated by pyuic so my solution assumes that you must recreate that script that must be called gui.py:
import shlex
import sys
from PyQt5.QtCore import QObject, QProcess
from PyQt5.QtWidgets import QApplication, QMainWindow
from gui import Ui_MainWindow
class Manager(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._process = QProcess()
working_directory = r"c://tensorflow_1//models//research//object_detection"
self.process.setWorkingDirectory(working_directory)
self.process.setProgram(sys.executable)
args = shlex.split(
"train.py --logtostderr --train_dir=training/ --pipeline_config_path=training/faster_rcnn_inception_v2_pets.config"
)
self.process.setArguments(args)
#property
def process(self):
return self._process
def start(self):
if self.process.state() == QProcess.NotRunning:
self.process.start()
def stop(self):
if self.process.state() != QProcess.NotRunning:
self.process.kill()
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.manager = Manager()
self.Start_train.clicked.connect(self.manager.start)
self.Stop_train.clicked.connect(self.manager.stop)
self.manager.process.stateChanged.connect(self._handle_stateChanged)
def _handle_stateChanged(self, state):
self.Start_train.setEnabled(state != QProcess.Running)
self.Stop_train.setEnabled(state == QProcess.Running)
def main():
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

How to keep the dialog open after message box in PyQt5?

I have a QmainWindow called "first page" it has two buttons in it "Signin" and "Signup".Both buttons will open Qdialogs. In my sign in dialog I have two line edits that takes user's input and two buttons "cancel" and "ok". When "ok" is clicked I want to check some validations such as If the line edits are empty then show a QmessageBox on top of the dialog and then close the messagebox after user click "ok" on messagebox.
MY PROBLEM: When the messagebox opens, the dialog closes.
P.S: I've been implementing my code by modifing the QtDesigner code which I just found out was the worst thing to do! so I'm doing everything again. I didn't have this problem back then.
my code:
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
from first_page import Ui_first_page
from signin_page import Ui_signin_page
from signup_page import Ui_signup_page
class SignIn(qtw.QDialog):
def __init__(self, *args, **kwargs):
"""SignIn constructor."""
super().__init__(*args, **kwargs)
self.ui = Ui_signin_page()
self.ui.setupUi(self)
# Connects the function that inputs data for sign in to the OK button.
self.ui.signin_buttons.accepted.connect(self.sign_in_authenticate)
def sign_in_authenticate(self):
"""Inputs the user's username and password for sign in process and checks validations"""
input_username = self.ui.signin_username_lineEdit.text()
input_password = self.ui.signin_password_lineEdit.text()
# Check if both are filled
if input_username == "":
#MY PROBLEM IS HERE
qtw.QMessageBox.critical(self,"Note", "Please input your username.")
elif input_password == "":
qtw.QMessageBox.critical(self, "Note", "Please input your password.")
else:
# Checks validations and opens mainpage
open_main_page(self, username, status)
def open_main_page(self, username, status):
""" Opens the main page after sign in"""
pass
class FirstPage(qtw.QMainWindow):
def __init__(self, *args, **kwargs):
"""FirstPage constructor."""
super().__init__(*args, **kwargs)
self.ui = Ui_first_page()
self.ui.setupUi(self)
# Connecting the buttons to open Sign in and Sign out pages
self.ui.first_page_sign_in_button.clicked.connect(self.open_sign_in_page)
self.ui.first_page_sign_up_button.clicked.connect(self.open_sign_up_page)
self.show()
def open_sign_in_page(self):
self.sigin = SignIn()
self.sigin.show()
def open_sign_up_page(self):
self.signup = SignUp()
self.signup.show()
class SignUp(qtw.QDialog):
#Doesn't do anything yet.
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.ui = Ui_signup_page()
self.ui.setupUi(self)
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
window = FirstPage()
sys.exit(app.exec_())
I'm very new so if I'm doing anything very wrong please point it out.
thanks in advance.
Mini example of my problem:
Main code:
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
from mainwindow import Ui_MainWindow
from dialog import Ui_signin_Dialog
class SignIn(qtw.QDialog):
def __init__(self, *args, **kwargs):
"""SignIn constructor."""
super().__init__(*args, **kwargs)
self.ui = Ui_signin_Dialog()
self.ui.setupUi(self)
# Connects the function that inputs data for sign in to the OK button.
self.ui.signin_buttonBox.accepted.connect(self.sign_in_authenticate)
def sign_in_authenticate(self):
input_text = self.ui.input_lineEdit.text()
if input_text == "":
qtw.QMessageBox.critical(self,"Note", "Please input your data.")
class FirstPage(qtw.QMainWindow):
def __init__(self, *args, **kwargs):
"""FirstPage constructor."""
super().__init__(*args, **kwargs)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# Connecting the buttons to open Sign in and Sign out pages
self.ui.signin_pushButton.clicked.connect(self.open_sign_in_page)
self.show()
def open_sign_in_page(self):
self.sigin = SignIn()
self.sigin.show()
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
window = FirstPage()
sys.exit(app.exec_())
Ui_MainWindow:
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(311, 293)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.signin_pushButton = QtWidgets.QPushButton(self.centralwidget)
self.signin_pushButton.setGeometry(QtCore.QRect(100, 110, 89, 25))
self.signin_pushButton.setObjectName("signin_pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 311, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
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", "MainWindow"))
self.signin_pushButton.setText(_translate("MainWindow", "Sign in "))
Ui_signin_Dialog
class Ui_signin_Dialog(object):
def setupUi(self, signin_Dialog):
signin_Dialog.setObjectName("signin_Dialog")
signin_Dialog.resize(400, 300)
self.signin_buttonBox = QtWidgets.QDialogButtonBox(signin_Dialog)
self.signin_buttonBox.setGeometry(QtCore.QRect(290, 230, 80, 56))
self.signin_buttonBox.setOrientation(QtCore.Qt.Vertical)
self.signin_buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.signin_buttonBox.setObjectName("signin_buttonBox")
self.input_lineEdit = QtWidgets.QLineEdit(signin_Dialog)
self.input_lineEdit.setGeometry(QtCore.QRect(140, 130, 113, 25))
self.input_lineEdit.setObjectName("input_lineEdit")
self.retranslateUi(signin_Dialog)
self.signin_buttonBox.accepted.connect(signin_Dialog.accept)
self.signin_buttonBox.rejected.connect(signin_Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(signin_Dialog)
def retranslateUi(self, signin_Dialog):
_translate = QtCore.QCoreApplication.translate
signin_Dialog.setWindowTitle(_translate("signin_Dialog", "Dialog"))
I want the Qmessagebox to open like a popup message instead of closing the dialog.
Try it:
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
from PyQt5 import QtCore, QtGui, QtWidgets # +++
#from mainwindow import Ui_MainWindow
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(311, 293)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.signin_pushButton = QtWidgets.QPushButton(self.centralwidget)
self.signin_pushButton.setGeometry(QtCore.QRect(100, 110, 89, 25))
self.signin_pushButton.setObjectName("signin_pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 311, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
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", "MainWindow"))
self.signin_pushButton.setText(_translate("MainWindow", "Sign in "))
#from dialog import Ui_signin_Dialog
class Ui_signin_Dialog(object):
def setupUi(self, signin_Dialog):
signin_Dialog.setObjectName("signin_Dialog")
signin_Dialog.resize(400, 300)
self.signin_buttonBox = QtWidgets.QDialogButtonBox(signin_Dialog)
self.signin_buttonBox.setGeometry(QtCore.QRect(290, 230, 80, 56))
self.signin_buttonBox.setOrientation(QtCore.Qt.Vertical)
self.signin_buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.signin_buttonBox.setObjectName("signin_buttonBox")
self.input_lineEdit = QtWidgets.QLineEdit(signin_Dialog)
self.input_lineEdit.setGeometry(QtCore.QRect(140, 130, 113, 25))
self.input_lineEdit.setObjectName("input_lineEdit")
self.retranslateUi(signin_Dialog)
# self.signin_buttonBox.accepted.connect(signin_Dialog.accept) # ---
self.signin_buttonBox.rejected.connect(signin_Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(signin_Dialog)
def retranslateUi(self, signin_Dialog):
_translate = QtCore.QCoreApplication.translate
signin_Dialog.setWindowTitle(_translate("signin_Dialog", "Dialog"))
class SignIn(qtw.QDialog):
def __init__(self, *args, **kwargs):
"""SignIn constructor."""
super().__init__(*args, **kwargs)
self.ui = Ui_signin_Dialog()
self.ui.setupUi(self)
# Connects the function that inputs data for sign in to the OK button.
self.ui.signin_buttonBox.accepted.connect(self.sign_in_authenticate)
def sign_in_authenticate(self):
input_text = self.ui.input_lineEdit.text()
if input_text == "":
qtw.QMessageBox.critical(self,"Note", "Please input your data.")
else: # +++
self.accept() # +++
class FirstPage(qtw.QMainWindow):
def __init__(self, *args, **kwargs):
"""FirstPage constructor."""
super().__init__(*args, **kwargs)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# Connecting the buttons to open Sign in and Sign out pages
self.ui.signin_pushButton.clicked.connect(self.open_sign_in_page)
self.show()
def open_sign_in_page(self):
self.sigin = SignIn()
self.sigin.show()
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
window = FirstPage()
sys.exit(app.exec_())

Remove QTextDocument which is inside QTextEdit

I am trying to remove QTextDocument which is inside the QTextEdit but I am not able to do it. I used clear() method of QTextEdit but it won't clear the QTextEdit area. I also try to overwrite new document on previous by setting overwrite flag of QTextEdit but that doesn't work as well.
def create_doc(body_text):
doc = QTextDocument()
doc.setDocumentMargin(10)
cursor = QTextCursor(doc)
cursor.movePosition(QTextCursor.Start)
block_fmt = QTextBlockFormat()
block_fmt.setBottomMargin(6)
block_fmt.setAlignment(QtCore.Qt.AlignJustify)
para_fmt = QTextCharFormat()
para_fmt.setFontWeight(QFont.Normal)
para_fmt.setFontPointSize(12)
para_fmt.setFontFamilies(["Raleway", "Arial"])
cursor.insertBlock(block_fmt)
cursor.insertText(body_text, para_fmt)
cursor.movePosition(QTextCursor.End)
return doc
class AppUI(QtWidgets.QMainWindow):
def __init__(self):
super(AppUI, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.qTextEditArea.setOverwriteMode(True)
self.ui.button.clicked.connect(lambda : self.add_text("some text")
def add_text(self, text):
self.ui.qTextEditArea.clear()
document = create_doc(doc)
self.ui.qTextEditArea.setDocument(document)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ui = AppUI()
ui.show()
sys.exit(app.exec_())
If you know how to do it please help. Thanks
QTextEdit Class
document : QTextDocument*
This property holds the underlying document of the text editor.
Note: The editor does not take ownership of the document unless it is the document's parent object. The parent object of the provided document remains the owner of the object. If the previously assigned document is a child of the editor then it will be deleted.
Access functions:
QTextDocument * document() const
void setDocument(QTextDocument *document)
main.py
import sys
from PyQt5.Qt import *
from textEditArea import Ui_MainWindow
def create_doc(body_text):
doc = QTextDocument()
doc.setDocumentMargin(10)
cursor = QTextCursor(doc)
cursor.movePosition(QTextCursor.Start)
block_fmt = QTextBlockFormat()
block_fmt.setBottomMargin(6)
block_fmt.setAlignment(Qt.AlignJustify)
para_fmt = QTextCharFormat()
para_fmt.setFontWeight(QFont.Normal)
para_fmt.setFontPointSize(12)
para_fmt.setFontFamilies(["Raleway", "Arial"]) # This function was introduced in Qt 5.13.
cursor.insertBlock(block_fmt)
cursor.insertText(body_text, para_fmt)
cursor.movePosition(QTextCursor.End)
return doc
class AppUI(QMainWindow):
def __init__(self):
super(AppUI, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# self.ui.qTextEditArea.setOverwriteMode(True)
self.ui.button.clicked.connect(lambda : self.add_text("some text"))
def add_text(self, text):
# self.ui.qTextEditArea.clear()
# document = create_doc(doc) # ??? (doc)
document = create_doc(text) # +++ text
self.ui.qTextEditArea.setDocument(document)
if __name__ == "__main__":
app = QApplication(sys.argv)
ui = AppUI()
ui.show()
sys.exit(app.exec_())
textEditArea.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(456, 304)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.qTextEditArea = QtWidgets.QTextEdit(self.centralwidget)
self.qTextEditArea.setObjectName("qTextEditArea")
self.gridLayout.addWidget(self.qTextEditArea, 0, 0, 1, 1)
self.button = QtWidgets.QPushButton(self.centralwidget)
self.button.setObjectName("button")
self.gridLayout.addWidget(self.button, 1, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 456, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
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", "MainWindow"))
self.button.setText(_translate("MainWindow", "PushButton"))

Categories

Resources