QSystemTrayIcon makes python crash - python

QSystemTrayIcon makes the application crash. I dont get an error in the console. Windows says AppHangB1. However, sometimes it works a few times, sometimes it crashes when first showing the context menu.
Edit:
This is my code broke down to the most important things:
class SystemTrayIcon(QSystemTrayIcon):
def __init__(self, icon, parent=None):
QSystemTrayIcon.__init__(self, icon, parent)
menu = QMenu()
showAction = menu.addAction("Fenster anzeigen")
exitAction = menu.addAction("Beenden")
menu.setStyleSheet("QMenu{background-color:white; margin:2px; "
"font: 75 10pt Trebuchet MS;} "
"QMenu::item:selected{background-color:#8e0000;}")
self.setContextMenu(menu)
self.setToolTip("KOSE")
exitAction.triggered.connect(self.exit)
showAction.triggered.connect(self.show_window)
def exit(self):
sys.exit()
def show_window(self):
if (loading_screen_window.windowOpacity() == 0.0):
loading_screen_window.fadeIN()
class CancelWindow(QWidget):
def __init__(self):
super(CancelWindow, self).__init__()
loader = QUiLoader()
file = QFile("cancel.ui")
file.open(QFile.ReadOnly)
global cancel_screen
cancel_screen = loader.load(file, self)
file.close()
self.initUI()
def initUI(self):
#some init stuff like window pos, button- function connects...
def fadeIN(self):
#window fade in animation
def fadeOUT(self, exit):
#fade out animation
def ja(self):
#button func
self.fadeOUT(True)
def nein(self):
#button func
self.fadeOUT(False)
kunden_screen.lbl_opacity.hide()
kunden_screen.setEnabled(True)
def exit_app(self):
QCoreApplication.exit()
class LoadingScreen(QWidget):
def __init__(self):
super(LoadingScreen, self).__init__()
loader = QUiLoader()
file = QFile("loading_screen.ui")
file.open(QFile.ReadOnly)
global loading_screen
loading_screen = loader.load(file, self)
file.close()
self.initUI()
#Config Thread
self.config_thread = config()
self.config_thread.finished.connect(self.config_finished)
#Config Thread starten
self.config_thread.start()
def initUI(self):
#window init
def fadeIN(self):
#fade animation
def fadeOUT(self):
#fade animation
def config_finished(self):
#function thats called when config thread is finished
class KundenSelect(QWidget):
connected = 0
cursor = None
conn_database = None
def __init__(self):
super(KundenSelect, self).__init__()
loader = QUiLoader()
file = QFile("kunden.ui")
file.open(QFile.ReadOnly)
global kunden_screen
kunden_screen = loader.load(file, self)
file.close()
self.initUI()
def initUI(self):
#window init
class config(QThread):
def run(self):
#config thread, reading files, database connection ...
app = QApplication(sys.argv)
global loading_screen_window
loading_screen_window = LoadingScreen()
global kunden_screen_window
kunden_screen_window = KundenSelect()
kunden_screen.lbl_opacity.hide()
kunden_screen_window.hide()
global cancel_screen_window
cancel_screen_window = CancelWindow()
cancel_screen_window.hide()
trayIcon = SystemTrayIcon(QIcon("icon.png"), parent=app)
trayIcon.show()
sys.exit(app.exec_())
I've googled it, but didnt find any solution. Im using PySide2 and Qt5.
Thanks

This code seems to be working just changed the signal/slot syntax
class SystemTrayIcon(QSystemTrayIcon):
def __init__(self, icon, parent=None):
QSystemTrayIcon.__init__(self, icon, parent)
menu = QMenu()
showAction = menu.addAction("Fenster anzeigen")
exitAction = menu.addAction("Beenden")
menu.setStyleSheet("QMenu{background-color:white; margin:2px; "
"font: 75 10pt Trebuchet MS;} "
"QMenu::item:selected{background-color:#8e0000;}")
self.setContextMenu(menu)
self.setToolTip("KOSE")
exitAction.triggered.connect(self.exit)
showAction.triggered.connect(self.show_window)
def exit(self):
sys.exit()
def show_window(self):
print("xxxxxx")
# if (loading_screen_window.windowOpacity() == 0.0):
# loading_screen_window.fadeIN()
app = QApplication(sys.argv)
trayIcon = SystemTrayIcon(QIcon("icon.png"), parent=app)
trayIcon.show()
sys.exit(app.exec_())
Using Pyside (not Pyside2), but I don't think it would be that different here.

Related

How to update UI with output from QProcess loop without the UI freezing?

I am wanting to have a list of commands being processed through a QProcess and have its output be appended to a textfield I have. I've found a these two pages that seems to do each of the things i need (updating the UI, and not freezing the UI via QThread):
Printing QProcess Stdout only if it contains a Substring
https://nikolak.com/pyqt-threading-tutorial/
So i tried to combine these two....
import sys
from PySide import QtGui, QtCore
class commandThread(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
self.cmdList = None
self.process = QtCore.QProcess()
def __del__(self):
self.wait()
def command(self):
# print 'something'
self.process.start('ping', ['127.0.0.1'])
processStdout = str(self.process.readAll())
return processStdout
def run(self):
for i in range(3):
messages = self.command()
self.emit(QtCore.SIGNAL('dataReady(QString)'), messages)
# self.sleep(1)
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.initUI()
def dataReady(self,outputMessage):
cursorOutput = self.output.textCursor()
cursorSummary = self.summary.textCursor()
cursorOutput.movePosition(cursorOutput.End)
cursorSummary.movePosition(cursorSummary.End)
# Update self.output
cursorOutput.insertText(outputMessage)
# Update self.summary
for line in outputMessage.split("\n"):
if 'TTL' in line:
cursorSummary.insertText(line)
self.output.ensureCursorVisible()
self.summary.ensureCursorVisible()
def initUI(self):
layout = QtGui.QHBoxLayout()
self.runBtn = QtGui.QPushButton('Run')
self.runBtn.clicked.connect(self.callThread)
self.output = QtGui.QTextEdit()
self.summary = QtGui.QTextEdit()
layout.addWidget(self.runBtn)
layout.addWidget(self.output)
layout.addWidget(self.summary)
centralWidget = QtGui.QWidget()
centralWidget.setLayout(layout)
self.setCentralWidget(centralWidget)
# self.process.started.connect(lambda: self.runBtn.setEnabled(False))
# self.process.finished.connect(lambda: self.runBtn.setEnabled(True))
def callThread(self):
self.runBtn.setEnabled(False)
self.get_thread = commandThread()
# print 'this this running?'
self.connect(self.get_thread, QtCore.SIGNAL("dataReady(QString)"), self.dataReady)
self.connect(self.get_thread, QtCore.SIGNAL("finished()"), self.done)
def done(self):
self.runBtn.setEnabled(True)
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The problem is that once I click the "Run" button the textfield on the right doesn't seem to populate, and i am no longer getting any errors so I am not sure what is happening.
I tried referring to this page as well but I think i am already emulating what it is describing...?
https://www.qtcentre.org/threads/46056-QProcess-in-a-loop-works-but
Ultimately what I want to build is for a main window to submit a series of commands via subprocess/QProcess, and open up a little log window that constantly updates it on the progress via displaying the console output. Similar to what you kind of see in like Installer packages...
I feel like i am so close to an answer, yet so far away. Is anyone able to chime in on this?
EDIT: so to answer eyllanesc's question, the list of commands has to be run one after the previous one has completed, as the command i plan to use will be very CPU intensive, and i cannot have more than one process of it running. also the time of each command completing will completely vary so I can't just have a arbitrary hold like with time.sleep() as some may complete quicker/slower than others. so ideally figuring out when the process has finished should kickstart another command (which is why i have a for loop in this example to represent that).
i also decided to use threads because apparently that was one way of preventing the UI to freeze while the process was running,so i assumed i needed to utilize this to have a sort of live feed/update in the text field.
the other thing is in the UI i would ideally in addition to updating a text field with console logs, i would want it to have some sort of label that gets updated that says something like "2 of 10 jobs completed". so something like this:
It would be nice too when before a new command is being processed a custom message can be appended to the text field indicating what command is being run...
UPDATE: apologies for taking so long to post an update on this, but based on eyllanesc's answer, I was able to figure out how to make this open a separate window and run the "ping" commands. here is the example code I have made to achieve my results in my main application:
from PySide import QtCore, QtGui
class Task:
def __init__(self, program, args=None):
self._program = program
self._args = args or []
#property
def program(self):
return self._program
#property
def args(self):
return self._args
class SequentialManager(QtCore.QObject):
started = QtCore.Signal()
finished = QtCore.Signal()
progressChanged = QtCore.Signal(int)
dataChanged = QtCore.Signal(str)
#^ this is how we can send a signal and can declare what type
# of information we want to pass with this signal
def __init__(self, parent=None):
super(SequentialManager, self).__init__(parent)
self._progress = 0
self._tasks = []
self._process = QtCore.QProcess(self)
self._process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self._process.finished.connect(self._on_finished)
self._process.readyReadStandardOutput.connect(self._on_readyReadStandardOutput)
def execute(self, tasks):
self._tasks = iter(tasks)
#this 'iter()' method creates an iterator object
self.started.emit()
self._progress = 0
self.progressChanged.emit(self._progress)
self._execute_next()
def _execute_next(self):
try:
task = next(self._tasks)
except StopIteration:
return False
else:
self._process.start(task.program, task.args)
return True
# QtCore.Slot()
#^ we don't need this line here
def _on_finished(self):
self._process_task()
if not self._execute_next():
self.finished.emit()
# #QtCore.Slot()
def _on_readyReadStandardOutput(self):
output = self._process.readAllStandardOutput()
result = output.data().decode()
self.dataChanged.emit(result)
def _process_task(self):
self._progress += 1
self.progressChanged.emit(self._progress)
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.outputWindow = outputLog(parentWindow=self)
self._button = QtGui.QPushButton("Start")
central_widget = QtGui.QWidget()
lay = QtGui.QVBoxLayout(central_widget)
lay.addWidget(self._button)
self.setCentralWidget(central_widget)
self._button.clicked.connect(self.showOutput)
def showOutput(self):
self.outputWindow.show()
self.outputWindow.startProcess()
#property
def startButton(self):
return self._button
class outputLog(QtGui.QWidget):
def __init__(self, parent=None, parentWindow=None):
QtGui.QWidget.__init__(self,parent)
self.parentWindow = parentWindow
self.setWindowTitle('Render Log')
self.setMinimumSize(225, 150)
self.renderLogWidget = QtGui.QWidget()
lay = QtGui.QVBoxLayout(self.renderLogWidget)
self._textedit = QtGui.QTextEdit(readOnly=True)
self._progressbar = QtGui.QProgressBar()
self._button = QtGui.QPushButton("Close")
self._button.clicked.connect(self.windowClose)
lay.addWidget(self._textedit)
lay.addWidget(self._progressbar)
lay.addWidget(self._button)
self._manager = SequentialManager(self)
self.setLayout(lay)
def startProcess(self):
self._manager.progressChanged.connect(self._progressbar.setValue)
self._manager.dataChanged.connect(self.on_dataChanged)
self._manager.started.connect(self.on_started)
self._manager.finished.connect(self.on_finished)
self._progressbar.setFormat("%v/%m")
self._progressbar.setValue(0)
tasks = [
Task("ping", ["8.8.8.8"]),
Task("ping", ["8.8.8.8"]),
Task("ping", ["8.8.8.8"]),
Task("ping", ["8.8.8.8"]),
Task("ping", ["8.8.8.8"]),
Task("ping", ["8.8.8.8"]),
]
self._progressbar.setMaximum(len(tasks))
self._manager.execute(tasks)
#QtCore.Slot()
def on_started(self):
self._button.setEnabled(False)
self.parentWindow.startButton.setEnabled(False)
#QtCore.Slot()
def on_finished(self):
self._button.setEnabled(True)
#QtCore.Slot(str)
def on_dataChanged(self, message):
if message:
cursor = self._textedit.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(message)
self._textedit.ensureCursorVisible()
def windowClose(self):
self.parentWindow.startButton.setEnabled(True)
self.close()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
i still don't really understand the use of the QtCore.Slot() decorators as when I commented them out it didn't really seem to change the result. But i kept them in just to be safe.
It is not necessary to use threads in this case since QProcess is executed using the event loop. The procedure is to launch a task, wait for the finishes signal, get the result, send the result, and execute the next task until all the tasks are finished. The key to the solution is to use the signals and distribute the tasks with an iterator.
Considering the above, the solution is:
from PySide import QtCore, QtGui
class Task:
def __init__(self, program, args=None):
self._program = program
self._args = args or []
#property
def program(self):
return self._program
#property
def args(self):
return self._args
class SequentialManager(QtCore.QObject):
started = QtCore.Signal()
finished = QtCore.Signal()
progressChanged = QtCore.Signal(int)
dataChanged = QtCore.Signal(str)
def __init__(self, parent=None):
super(SequentialManager, self).__init__(parent)
self._progress = 0
self._tasks = []
self._process = QtCore.QProcess(self)
self._process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self._process.finished.connect(self._on_finished)
self._process.readyReadStandardOutput.connect(self._on_readyReadStandardOutput)
def execute(self, tasks):
self._tasks = iter(tasks)
self.started.emit()
self._progress = 0
self.progressChanged.emit(self._progress)
self._execute_next()
def _execute_next(self):
try:
task = next(self._tasks)
except StopIteration:
return False
else:
self._process.start(task.program, task.args)
return True
QtCore.Slot()
def _on_finished(self):
self._process_task()
if not self._execute_next():
self.finished.emit()
#QtCore.Slot()
def _on_readyReadStandardOutput(self):
output = self._process.readAllStandardOutput()
result = output.data().decode()
self.dataChanged.emit(result)
def _process_task(self):
self._progress += 1
self.progressChanged.emit(self._progress)
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self._button = QtGui.QPushButton("Start")
self._textedit = QtGui.QTextEdit(readOnly=True)
self._progressbar = QtGui.QProgressBar()
central_widget = QtGui.QWidget()
lay = QtGui.QVBoxLayout(central_widget)
lay.addWidget(self._button)
lay.addWidget(self._textedit)
lay.addWidget(self._progressbar)
self.setCentralWidget(central_widget)
self._manager = SequentialManager(self)
self._manager.progressChanged.connect(self._progressbar.setValue)
self._manager.dataChanged.connect(self.on_dataChanged)
self._manager.started.connect(self.on_started)
self._manager.finished.connect(self.on_finished)
self._button.clicked.connect(self.on_clicked)
#QtCore.Slot()
def on_clicked(self):
self._progressbar.setFormat("%v/%m")
self._progressbar.setValue(0)
tasks = [
Task("ping", ["8.8.8.8"]),
Task("ping", ["8.8.8.8"]),
Task("ping", ["8.8.8.8"]),
]
self._progressbar.setMaximum(len(tasks))
self._manager.execute(tasks)
#QtCore.Slot()
def on_started(self):
self._button.setEnabled(False)
#QtCore.Slot()
def on_finished(self):
self._button.setEnabled(True)
#QtCore.Slot(str)
def on_dataChanged(self, message):
if message:
cursor = self._textedit.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(message)
self._textedit.ensureCursorVisible()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

How to change a variable from a class and be able to import it to another file?

I am making a GUI and have a code with several files and uses a controller file to switch between the files. However, I need several of the variables to be available in the other files and also want an own file where I can keep track of the values for all the variables.
I have now instantiated the variables on top of the file, and tried to change the values in the class below, but if I then import to another file it will only give the value which was instantiated first (which is fair since I did not call the class, but is a problem).
Please help.
Under i some of the code:
From file firstwindow
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
LEVELS = 2
NUM_ICE = 4
NUM_CONES = 8
class Login(QtWidgets.QWidget):
switch_window = QtCore.pyqtSignal()
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setupUi(self)
self.setWindowTitle('First')
def setupUi(self, FirstWindow):
FirstWindow.setObjectName("First")
FirstWindow.setEnabled(True)
FirstWindow.resize(675,776)
FirstWindow.setFocusPolicy(QtCore.Qt.TabFocus)
layout = QtWidgets.QGridLayout()
self.spinBoxNUM_ICE = QtWidgets.QSpinBox()
self.spinBoxNUM_CONES = QtWidgets.QSpinBox()
self.spinBoxLEVELS = QtWidgets.QSpinBox()
layout.addWidget(self.spinBoxNUM_MASTERS,1,2)
layout.addWidget(self.spinBoxNUM_SLAVES,2,2)
layout.addWidget(self.spinBoxPRIORITY_LEVELS,11,2)
#CONTINUE AND QUIT BUTTON
self.QuitButton = QtWidgets.QPushButton("Quit")
self.QContinueButton = QtWidgets.QPushButton("Continue")
#actions
self.QuitButton.clicked.connect(FirstWindow.close)
self.QContinueButton.clicked.connect(self.login)
def login(self):
#global NUM_ICE
self.NUM_ICE = self.spinBoxNUM_ICE.value()
global NUM_CONES
NUM_CONES = self.spinBoxNUM_CONES.value()
global LEVELS
LEVELS = self.spinBoxLEVELS.value()
self.switch_window.emit()
And in the controller file
class Controller:
def __init__(self):
pass
def show_login(self):
self.login = Login()
self.login.switch_window.connect(self.show_main)
self.login.show()
def show_main(self):
self.window = MainWindow()
self.window.switch_window.connect(self.show_window_two)
self.login.close()
self.window.show()
And in the MainWindow file where I want to use LEVELS
import sys
from PyQt5 import QtCore, QtWidgets
from firstwindow import LEVELS
class MainWindow(QtWidgets.QWidget):
switch_window = QtCore.pyqtSignal()
#switch_window = QtCore.pyqtSignal(str)
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setupUi(self, LEVELS)
self.setWindowTitle('PriorityMap')
def setupUi(self, PriorityMap, LEVELS):
PriorityMap.setObjectName("First")
PriorityMap.setEnabled(True)
PriorityMap.resize(675,776)
PriorityMap.setFocusPolicy(QtCore.Qt.TabFocus)
layout = QtWidgets.QGridLayout()
#CREATING ELEMENTS
for i in range(0,LEVELS+2):
for j in range(0,5):
if (i==0 and j!=0):
layout.addWidget(QtWidgets.QLabel(str(j-1)),i,j)
elif (j==0 and i!=0):
layout.addWidget(QtWidgets.QLabel("LEVEL"+str(i-1)),i,j)
else:
layout.addWidget(QtWidgets.QPushButton(str(i)+","+str(j)),i,j)
#CONTINUE AND QUIT BUTTON
self.QuitButton = QtWidgets.QPushButton("Quit")
self.QContinueButton = QtWidgets.QPushButton("Continue")
#actions
self.QuitButton.clicked.connect(PriorityMap.close)
self.QContinueButton.clicked.connect(self.switch)
#LAYOUT
layout.addWidget(self.QuitButton,15,1)
layout.addWidget(self.QContinueButton,15,2)
self.setLayout(layout)
def switch(self):
self.switch_window.emit()
Avoid abusing the global variables(1), and in this case it is not necessary, you must make the dynamic creation of widgets a moment before making the change in the show_main method:
class Controller:
def show_login(self):
self.login = Login()
self.login.switch_window.connect(self.show_main)
self.login.show()
def show_main(self):
self.window = MainWindow()
levels = self.login.spinBoxLEVELS.value()
self.window.setLevels(levels)
self.window.switch_window.connect(self.show_window_two)
self.login.close()
self.window.show()
class MainWindow(QtWidgets.QWidget):
switch_window = QtCore.pyqtSignal()
# switch_window = QtCore.pyqtSignal(str)
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.setupUi(self)
self.setWindowTitle('PriorityMap')
def setupUi(self, PriorityMap):
PriorityMap.setObjectName("First")
PriorityMap.setEnabled(True)
PriorityMap.resize(675,776)
PriorityMap.setFocusPolicy(QtCore.Qt.TabFocus)
layout = QtWidgets.QVBoxLayout(self)
self.m_content_widget = QtWidgets.QWidget()
layout.addWidget(self.m_content_widget, stretch=1)
#CONTINUE AND QUIT BUTTON
self.QuitButton = QtWidgets.QPushButton("Quit")
self.QContinueButton = QtWidgets.QPushButton("Continue")
#actions
self.QuitButton.clicked.connect(PriorityMap.close)
self.QContinueButton.clicked.connect(self.switch)
w = QtWidgets.QWidget()
hlay = QtWidgets.QHBoxLayout(w)
hlay.addWidget(self.QuitButton)
hlay.addWidget(self.QContinueButton)
layout.addWidget(w, alignment=QtCore.Qt.AlignRight)
def setLevels(self, levels):
layout = QtWidgets.QGridLayout(self.m_content_widget)
for i in range(0,levels+2):
for j in range(0, 5):
if (i==0 and j!=0):
layout.addWidget(QtWidgets.QLabel(str(j-1)),i,j)
elif (j==0 and i!=0):
layout.addWidget(QtWidgets.QLabel("LEVEL"+str(i-1)),i,j)
else:
layout.addWidget(QtWidgets.QPushButton(str(i)+","+str(j)),i,j)
def switch(self):
self.switch_window.emit()
(1) Why are global variables evil?
Ended up doing this, which worked!
Instantiated all variables from login window before calling login.close. Also passed in variables needed in next function. In this way I was also able to create a function which prints out the parameters.
class Controller:
def __init__(self):
pass
def show_login(self):
self.login = Login()
self.login.switch_window.connect(self.show_main)
self.login.show()
def show_main(self):
self.LEVELS = self.login.LEVELS
self.window = MainWindow(self.LEVELS)
self.window.switch_window.connect(self.show_window_two)
self.login.close()
self.window.show()
def writetofile(Controller):
f = open("f.txt", "w+")
f.write("int LEVELS = %d;\n\n" %Controller.LEVELS)
f.close()

how to highlight text in python web browser like find text

I'm developing a webview browser in Python and PyQt5 and I want to know How to highlight particular text in Python and PyQt5 web browser. I want to highlight particular text like web find text in other browsers.
self.browser = QWebEngineView()
self.browser.HighlightAllOccurrences('hello world')
You have to use the findText method of QWebEngineView (or QWebEnginePage), in the following example there is a search bar when you press Ctrl + F that allows to make the search easier for the user:
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
class SearchPanel(QtWidgets.QWidget):
searched = QtCore.pyqtSignal(str, QtWebEngineWidgets.QWebEnginePage.FindFlag)
closed = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(SearchPanel, self).__init__(parent)
lay = QtWidgets.QHBoxLayout(self)
done_button = QtWidgets.QPushButton('&Done')
self.case_button = QtWidgets.QPushButton('Match &Case', checkable=True)
next_button = QtWidgets.QPushButton('&Next')
prev_button = QtWidgets.QPushButton('&Previous')
self.search_le = QtWidgets.QLineEdit()
self.setFocusProxy(self.search_le)
done_button.clicked.connect(self.closed)
next_button.clicked.connect(self.update_searching)
prev_button.clicked.connect(self.on_preview_find)
self.case_button.clicked.connect(self.update_searching)
for btn in (self.case_button, self.search_le, next_button, prev_button, done_button, done_button):
lay.addWidget(btn)
if isinstance(btn, QtWidgets.QPushButton): btn.clicked.connect(self.setFocus)
self.search_le.textChanged.connect(self.update_searching)
self.search_le.returnPressed.connect(self.update_searching)
self.closed.connect(self.search_le.clear)
QtWidgets.QShortcut(QtGui.QKeySequence.FindNext, self, activated=next_button.animateClick)
QtWidgets.QShortcut(QtGui.QKeySequence.FindPrevious, self, activated=prev_button.animateClick)
QtWidgets.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Escape), self.search_le, activated=self.closed)
#QtCore.pyqtSlot()
def on_preview_find(self):
self.update_searching(QtWebEngineWidgets.QWebEnginePage.FindBackward)
#QtCore.pyqtSlot()
def update_searching(self, direction=QtWebEngineWidgets.QWebEnginePage.FindFlag()):
flag = direction
if self.case_button.isChecked():
flag |= QtWebEngineWidgets.QWebEnginePage.FindCaseSensitively
self.searched.emit(self.search_le.text(), flag)
def showEvent(self, event):
super(SearchPanel, self).showEvent(event)
self.setFocus(True)
class Browser(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(Browser, self).__init__(parent)
self._view = QtWebEngineWidgets.QWebEngineView()
self.setCentralWidget(self._view)
self._view.load(QtCore.QUrl('https://doc.qt.io/qt-5/qwebengineview.html'))
self._search_panel = SearchPanel()
self.search_toolbar = QtWidgets.QToolBar()
self.search_toolbar.addWidget(self._search_panel)
self.addToolBar(QtCore.Qt.BottomToolBarArea, self.search_toolbar)
self.search_toolbar.hide()
self._search_panel.searched.connect(self.on_searched)
self._search_panel.closed.connect(self.search_toolbar.hide)
self.create_menus()
#QtCore.pyqtSlot(str, QtWebEngineWidgets.QWebEnginePage.FindFlag)
def on_searched(self, text, flag):
def callback(found):
if text and not found:
self.statusBar().show()
self.statusBar().showMessage('Not found')
else:
self.statusBar().hide()
self._view.findText(text, flag, callback)
def create_menus(self):
menubar = self.menuBar()
file_menu = menubar.addMenu('&File')
file_menu.addAction('&Find...', self.search_toolbar.show, shortcut=QtGui.QKeySequence.Find)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
w = Browser()
w.show()
sys.exit(app.exec_())

PyQt5: How to 'communicate' between QThread and some sub-class?

To this question I am referring to the answer from #eyllanesc in PyQt5: How to scroll text in QTextEdit automatically (animational effect)?
There #eyllanesc shows how to make the text auto scrolls smoothly using verticalScrollBar(). It works great.
For this question, I have added some extra lines, to use QThread to get the text.
What I want to achieve here: to let the QThread class 'communicate' with the AnimationTextEdit class, so that the scrolling time can be determined by the text-length. So that the programm stops, when the scrolling process ends.
I must say it is very very tricky task for me. I would like to show the programm flow first, as I imagined.
UPDATE: My code is as follows. It works, but...
Problem with the code: when the text stops to scroll, the time.sleep() still works. The App wait there till time.sleep() stops.
What I want to get: When the text stops to scroll, the time.sleep() runs to its end value.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
import time
import sqlite3
class AnimationTextEdit(QTextEdit):
# signal_HowLongIsTheText = pyqtSignal(int) # signal to tell the QThread, how long the text is
def __init__(self, *args, **kwargs):
QTextEdit.__init__(self, *args, **kwargs)
self.animation = QVariantAnimation(self)
self.animation.valueChanged.connect(self.moveToLine)
# def sent_Info_to_Thread(self):
# self.obj_Thread = Worker()
# self.signal_HowLongIsTheText.connect(self.obj_Thread.getText_HowLongIsIt)
# self.signal_HowLongIsTheText.emit(self.textLength)
# self.signal_HowLongIsTheText.disconnect(self.obj_Thread.getText_HowLongIsIt)
#pyqtSlot()
def startAnimation(self):
self.animation.stop()
self.animation.setStartValue(0)
self.textLength = self.verticalScrollBar().maximum()
# self.sent_Info_to_Thread()
self.animation.setEndValue(self.textLength)
self.animation.setDuration(self.animation.endValue()*4)
self.animation.start()
#pyqtSlot(QVariant)
def moveToLine(self, i):
self.verticalScrollBar().setValue(i)
class Worker(QObject):
finished = pyqtSignal()
textSignal = pyqtSignal(str)
# #pyqtSlot(int)
# def getText_HowLongIsIt(self, textLength):
# self.textLength = textLength
#pyqtSlot()
def getText(self):
longText = "\n".join(["{}: long text - auto scrolling ".format(i) for i in range(100)])
self.textSignal.emit(longText)
time.sleep(10)
# time.sleep(int(self.textLength / 100))
# My question is about the above line: time.sleep(self.textLength)
# Instead of giving a fixed sleep time value here,
# I want let the Worker Class know,
# how long it will take to scroll all the text to the end.
self.finished.emit()
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setFixedSize(600, 400)
self.initUI()
self.startThread()
def initUI(self):
self.txt = AnimationTextEdit(self)
self.btn = QPushButton("Start", self)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.txt)
self.layout.addWidget(self.btn)
self.btn.clicked.connect(self.txt.startAnimation)
def startThread(self):
self.obj = Worker()
self.thread = QThread()
self.obj.textSignal.connect(self.textUpdate)
self.obj.moveToThread(self.thread)
self.obj.finished.connect(self.thread.quit)
self.thread.started.connect(self.obj.getText)
self.thread.finished.connect(app.exit)
self.thread.start()
def textUpdate(self, longText):
self.txt.append(longText)
self.txt.moveToLine(0)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
Thanks for the help and hint. What have I done wrong?
Although in the animation the duration is established it is necessary to understand that this is not exact, this could vary for several reasons, so calculating using the sleep to wait for it to end in a certain time and just closing the application may fail.
If your main objective is that when the animation is finished the program execution is finished then you must use the finished QVariantAnimation signal to finish the execution of the thread, this signal is emited when it finishes executing.
class AnimationTextEdit(QTextEdit):
def __init__(self, *args, **kwargs):
QTextEdit.__init__(self, *args, **kwargs)
self.animation = QVariantAnimation(self)
self.animation.valueChanged.connect(self.moveToLine)
#pyqtSlot()
def startAnimation(self):
self.animation.stop()
self.animation.setStartValue(0)
self.textLength = self.verticalScrollBar().maximum()
self.animation.setEndValue(self.textLength)
self.animation.setDuration(self.animation.endValue()*4)
self.animation.start()
#pyqtSlot(QVariant)
def moveToLine(self, i):
self.verticalScrollBar().setValue(i)
class Worker(QObject):
textSignal = pyqtSignal(str)
#pyqtSlot()
def getText(self):
longText = "\n".join(["{}: long text - auto scrolling ".format(i) for i in range(100)])
self.textSignal.emit(longText)
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setFixedSize(600, 400)
self.initUI()
self.startThread()
def initUI(self):
self.txt = AnimationTextEdit(self)
self.btn = QPushButton("Start", self)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.txt)
self.layout.addWidget(self.btn)
self.btn.clicked.connect(self.txt.startAnimation)
def startThread(self):
self.obj = Worker()
self.thread = QThread()
self.obj.textSignal.connect(self.textUpdate)
self.obj.moveToThread(self.thread)
self.txt.animation.finished.connect(self.thread.quit)
self.thread.started.connect(self.obj.getText)
self.thread.finished.connect(app.exit)
self.thread.start()
def textUpdate(self, longText):
self.txt.append(longText)
self.txt.moveToLine(0)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())

Calling QMainWindow From Second QDialog

My PyQt application starts with Login screen. If password OK, a module-screen (with icons) appears. When user click some button, a QMainWindow will appears. But I can't do this because of qmainwindow object has no attribute '_exec' error. This is my code:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Main(QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
...
...
class Login(QDialog):
def __init__(self, parent=None):
super(Login, self).__init__(parent)
...
...
uyg=QApplication(sys.argv)
class icons(QDialog):
def __init__(self, parent=None):
super(icons, self).__init__(parent)
...
self.buton = QPushButton()
self.buton.pressed.connect(self.open)
...
def open(self):
dialogmain = Main()
dialogmain._exec() #or dialogmain.show() ???
self.accept()
self.close()
uyg.exec_()
if Login().exec_() == QDialog.Accepted:
dialog = icons()
dialog.exec_()
else:
uyg.quit()
What am I doing wrong? Thank you.
Lately i have done the similar work:I have a loging window and a main window ,and I used something like a FSM to switch between the loging and main window.
Let's say we have 3 state:loging,main,quit.
STATE_LOGING = 0
STATE_MAIN = 1
STATE_QUIT = 2
STATE_DESTROY = 3 #this is a flag
class CState():
sigSwitchState = pyqtSignal(int)
def __init__(self):
super(CState,self).__init__()
def start(self):
pass
def sendQuit(self,nextstate):
self.sigSwitch.emit(nextstate)
class CLoginState(CState):
def __init__(self):
super(CLoginState,self).__init__()
def start(self):
w = Loging()
w.show()
def whenPasswdOk(self):
self.sendQuit(STATE_MAIN)
class CMainState(CState):
def __init__(self):
super(CMainState,self).__init__()
def start(self):
w = MainWindow()
w.show()
def whenMainWindowQuit(self):
self.sendQuit(STATE_QUIT)
class CQuitState(CState):
def __init__(self):
super(CQuitState,self).__init__()
def start(self):
#do some clean stuff...
pass
def whenCleanDone(self):
self.sendQuit(STATE_DESTROY)
class CMainApp():
def __init__(self):
self.app = QApplication(sys.argv)
def __CreateState(state):
if state == STATE_LOGING:
s = CLoginState()
if state == STATE_MAIN:
s = CMainState()
#... same as other state
s.sigSwitchState.connect(self.procNextState)
def procNextState(self,state):
if state == STATE_DESTROY:
QApplication().exit()
s = self.__CreateState(state)
s.start()
def run(self):
self.procNextState(STATE_LOGING)
sys.exit(self.app.exec_())
if __name__ == "__main__":
app = CMainApp()
app.run()
Apart from the application object and QDrag, please pretend that exec() doesn't exist. It is an utterly confusing method that essentially never has to be used. Especially not by anyone new to Qt.
If you want to display any widget, simply show() it. If you want to be notified when a dialog was accepted, connect some code to its accepted() signal. That's all.

Categories