PyQt how to perform function BEFORE GUI resize event - python

In PyQt4, is there a way to suspend resize of a window until a function is completed?
My problem is that I have created a window with a text edit that might contain large amounts of text. Since I switched to working with a grid layout, the text edit gets resized as well, and when there is a lot of text, the application hangs. I tried overriding resizeEvent to clear text edit text at resize but the application still hangs, since it is clearing the text only AFTER resizing.
Other solutions are welcomed as well.
The python code (and a link to the .ui file):
import sys
from PyQt4 import QtGui, uic, QtCore
from PyQt4.QtGui import QDesktopWidget
qtCreatorMainWindowFile = "mainwindow.ui"
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorMainWindowFile)
class MainWindow(QtBaseClass, Ui_MainWindow):
def __init__(self):
QtBaseClass.__init__(self)
self.setupUi(self)
# Set window size to 4/5 of the screen dimensions
sg = QDesktopWidget().screenGeometry()
self.resize(sg.width()*4/5, sg.height()*4/5)
self.clearTextBrowserButton.clicked.connect(self.ClearTextBrowsers)
#staticmethod
def WriteToTextBrowser(string, text_browser):
cursor = text_browser.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(string)
text_browser.setTextCursor(cursor)
text_browser.ensureCursorVisible()
def ClearTextBrowsers(self):
self.textBrowser.clear()
# def resizeEvent(self,event):
# print "resize"
# self.ClearTextBrowsers()
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
for i in range(1,100000):
window.WriteToTextBrowser("TESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTESTTEST\r\n",window.textBrowser)
sys.exit(app.exec_())
The ui. file:
https://www.dropbox.com/s/y3hxp6mjhfpv2hy/mainwindow.ui?dl=0

I found a workaround that seems to work so far. I added an event filter that catches "Move" or "WindowStateChange" QEvents. These seem to happen before the actual resize (the prior works for clicking and stretching and the latter for maximizing/minimizing). The downside is that simply moving the window clears the text edit, but it is a price I'm willing to pay.
The added code (inside MainWindow):
self.installEventFilter(self)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.Move or event.type() == QtCore.QEvent.WindowStateChange):
self.file.write(self.textBrowser.toPlainText())
self.ClearTextBrowsers()
return QtBaseClass.eventFilter(self, source, event)

Related

Pushing QWidget Window to topmost in Python

I'm new to Python and have mostly learnt C# in the past. I am creating a QWidget class:
class Window(QWidget):
def __init__(self, gif, width, height):
super().__init__()
self.setGeometry(400, 200, width, height)
self.setWindowTitle("Python Run GIF Images")
self.setWindowIcon(QIcon('icons/qt.png'))
label = QLabel(self)
movie = QMovie(gif)
label.setMovie(movie)
movie.start()
And then define a function that creates a QApplication and then the Window:
def run_gif(gif, width, height):
app = QApplication([])
window = Window(gif, width, height)
window.show()
app.exec()
app.shutdown()
My issue is getting the gif to show topmost when it is launched. There is a Topmost property you can set on a Winform in C# which means no other window or other app you click on will cover the Window. This isn't essential but I want it to at least be shown above the code editor it is launched from so that the user doesn't have to select the Window to see the gif contained in the Window. I've spent hours looking at properties I can set either in the class or the method and not getting the result I need.
However, when I call the run_gif method for the second time, it does show it topmost, but along with the below exception (at least I think it's an exception from what I'm reading, it doesn't affect the running of the program other than printing to the console).
QApplication::regClass: Registering window class 'Qt640ScreenChangeObserverWindow' failed. (Class already exists.)
The only advice I can find on this error is not Python-specific and I don't really understand it. Maybe I'm being a bit ambitious for my early stage learning Python but not really seeing anything much on this error.
This is using PySide6.
Using setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, True) seems to be working for me so far.
Example:
from PySide6.QtWidgets import *
from PySide6.QtCore import *
from PySide6.QtGui import *
class Window(QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.layout = QVBoxLayout(self)
self.resize(200,100)
self.label = QLabel("Always on top...")
self.layout.addWidget(self.label)
self.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, True) # <--
if __name__ == '__main__':
app = QApplication([])
window = Window()
window.show()
app.exec()

Access QT Designer Objects Programmatically

Disclaimer: New to both python and qt designer
QT Designer 4.8.7
Python 3.4
PyCharm 5.0.3
Question - How do I add controls to the main form or a scroll area widget on the main form (created in QT Designer) programmatically?
I have created a MainWindow in qt designer and added my widgets. The following is the entire test program in PyCharm:
import sys
from PyQt4 import QtGui, QtCore, uic
from PyQt4.QtGui import *
from PyQt4.QtCore import *
qtCreatorFile = "programLauncher.ui"
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
# Cannot resize or maximize
self.setFixedSize(1045, 770)
# Add button test
self.dateLabel = QtGui.QLabel("Test")
self.pushButton = QtGui.QPushButton('Test button')
# self.scrollArea_programs.addWidget()
grid = QtGui.QGridLayout()
# self.scrollArea_programs.addWidget(self.pushButton)
grid.addWidget(self.dateLabel,0,0)
grid.addWidget(self.pushButton,0,1)
self.setLayout(grid)
self.pushButton_exit.clicked.connect(self.closeEvent)
def closeEvent(self):
QtGui.QApplication.quit()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
As you can see I tried to add controls to a grid but nothing shows up when the program runs - I have also tried to add a control to the scroll area. Can someone help me to just add 1 control to the scroll area at run time - so then I can know the proper way to do it or "a" proper way to do this.
Thanks in advance
Without having access to your programLauncher.ui and making minimal changes to your posted code, you can add your UI elements to the window like so:
from PyQt4 import QtGui
import sys
class MyApp(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
# Cannot resize or maximize
self.setFixedSize(1045, 770)
widget = QtGui.QWidget(self)
self.setCentralWidget(widget)
# Add button test
self.dateLabel = QtGui.QLabel("Test")
self.pushButton = QtGui.QPushButton('Test button')
grid = QtGui.QGridLayout()
grid.addWidget(self.dateLabel, 0, 0)
grid.addWidget(self.pushButton, 0, 1)
widget.setLayout(grid)
self.pushButton.clicked.connect(self.closeEvent)
def closeEvent(self, event):
QtGui.QApplication.quit()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
This will get the controls on the screen, although the layout leaves a lot to be desired. You may have to make modifications to this based on what's in your .ui file. One thing that you'll want to note in this example is that the QMainWindow needs a central widget (widget in the example above). You then set the layout on that widget.
You can use the designer to create your .ui file
The you can load it in your .py using something like:
from PyQt4 import QtCore, QtGui, uic
class my_win(QtGui.QMainWindow):
def __init__(self):
self.ui = uic.loadUi('my_ui.ui',self)
then you can access all your widgets with something like
self.ui.actionQuit.triggered.connect(QtGui.qApp.quit)
or
self.ui.my_button.triggered.connect(self.do_someting)
Thanks to JCVanHamme (the programLauncher.ui hint) and also outside help I now learned most of what I need to know to access MainWindow at run time. So for anyone interested in this beginner tip:
Take a blank form in QT Designer
Add a control
Run pyuic4 batch file
Take a look at the generated .py file to learn EVERYTHING about how to add controls.
Don't let the power go to your head - cheers

How to implement an always-on-top widget that hides when losing focus

I need to implement a top-level widget (fixed position on screen) that hides whenever the user clicks somewhere else in the desktop, but it should hide gradually, so the widget should still be visible when it happens. To simplify, I want something like the Windows 8 right sidebar, when the user pushes a button, like the Super key it comes up, when clicking somewhere else it fades away, but is still visible in the process.
This is, I want to have an always-on-top window that hides when it loses focus. I have implemented this in pyqt4 but it is not working.
import sys
from PyQt4 import QtGui, QtCore
class Signals(QtCore.QObject):
close = QtCore.pyqtSignal()
class Menu(QtGui.QWidget):
def __init__(self, signals):
super(Menu, self).__init__()
self.signals = signals
def mousePressEvent(self, event):
# Just simplificating the gradual hiding effect for the moment
self.signals.close.emit()
def focusOutEvent(self, event):
print "FocusOut"
self.signals.close.emit()
def main():
app = QtGui.QApplication(sys.argv)
signals = Signals()
signals.close.connect(app.quit)
w = Menu(signals)
w.setWindowFlags( QtCore.Qt.SplashScreen )
w.resize(200, 200)
w.move(0, 0)
w.setWindowTitle('Test')
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
However, this is not working. When I click somewhere else the widget won't enter the focusOutEvent. I've also tried installing an eventFilter but since the window is a SplashScreen it won't work.
Any ideas on how to tackle this?

pyQt signals/slots with QtDesigner

I'm trying to write a program that will interact with QGraphicsView. I want to gather mouse and keyboard events when the happen in the QGraphicsView. For example, if the user clicks on the QGraphicsView widget I will get the mouse position, something like that. I can hard code it rather easily, but I want to use QtDesigner because the UI will be changing frequently.
This is the code that I have for the gui.py. A simple widget with a QGraphicsView in it.
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_graphicsViewWidget(object):
def setupUi(self, graphicsViewWidget):
graphicsViewWidget.setObjectName(_fromUtf8("graphicsViewWidget"))
graphicsViewWidget.resize(400, 300)
graphicsViewWidget.setMouseTracking(True)
self.graphicsView = QtGui.QGraphicsView(graphicsViewWidget)
self.graphicsView.setGeometry(QtCore.QRect(70, 40, 256, 192))
self.graphicsView.setObjectName(_fromUtf8("graphicsView"))
self.retranslateUi(graphicsViewWidget)
QtCore.QMetaObject.connectSlotsByName(graphicsViewWidget)
def retranslateUi(self, graphicsViewWidget):
graphicsViewWidget.setWindowTitle(QtGui.QApplication.translate("graphicsViewWidget", "Form", None, QtGui.QApplication.UnicodeUTF8))
The code for the program:
#!/usr/bin/python -d
import sys
from PyQt4 import QtCore, QtGui
from gui import Ui_graphicsViewWidget
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_graphicsViewWidget()
self.ui.setupUi(self)
QtCore.QObject.connect(self.ui.graphicsView, QtCore.SIGNAL("moved"), self.test)
def mouseMoveEvent(self, event):
print "Mouse Pointer is currently hovering at: ", event.pos()
self.emit(QtCore.SIGNAL("moved"), event)
def test(self, event):
print('in test')
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())
When I run this code, it gives me the opposite of what I want. I get the mouse position everywhere except for inside the QGraphicsView.
I'm sure it's a problem with my QObject.connect. But every time I go back and read about signals and slots it makes sense but I can't get it.
Please help, I've been banging my head for the past few days now. I'm sorry if this as been asked before but I've been through all the threads on this topic and I can't get anywhere.
Thanks
The signal must come from the QGraphicsView object that was defined in the ui.
You can create a class derived from QGraphicsView like this
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class MyView(QGraphicsView):
moved = pyqtSignal(QMouseEvent)
def __init__(self, parent = None):
super(MyView, self).__init__(parent)
def mouseMoveEvent(self, event):
# call the base method to be sure the events are forwarded to the scene
super(MyView, self).mouseMoveEvent(event)
print "Mouse Pointer is currently hovering at: ", event.pos()
self.moved.emit(event)
Then, in the designer:
right-click on the QGraphicsView then Promote to
write the class name in the Promoted Class Name field (e.g. "MyView"),
write the file name where that class is in the Header file field but without the .py extension,
click on the Add button and then on the Promote button.
And you can regenerate your file gui.py with pyuic4.

Hide window from taskbar

I'm trying to minimize a window to the tray, but it seems it refuses to hide from the taskbar. I've spent a little time and distilled the problem code down to this. It's not mcuh so I'm wondering if I need something else to hide my app to tray in Windows 7.
import sys, os
from PyQt4 import uic
from PyQt4.QtGui import QMainWindow, QApplication
class MyClass(QMainWindow):
def __init__(self, parent = None):
QMainWindow.__init__(self, parent)
self.ui = uic.loadUi(os.path.join("gui", "timeTrackerClientGUI.ui"), self)
def hideEvent(self, event):
self.hide()
def showEvent(self, event):
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
wnd = MyClass()
wnd.show()
app.exec_()
It seems the application icon does hide but then another one pops up, If I click the taskbar icon multiple times I can get these two icons flickering, looks kind of like this for a splitsecond before the first one hides:
It took a quite ugly hack to get it working but here's the final code if anybody is interested, ph is my platform-specific module, you can use platform.name or similar function instead:
def hideEvent(self, event):
self.hide()
if ph.is_windows():
self.hidden = True
self.setWindowFlags(Qt.ToolTip)
def showEvent(self, event):
if ph.is_windows() and self.hidden:
self.setWindowFlags(Qt.Window)
self.hidden = False
self.show()
calling show/hide in showEvent()/hideEvent() doesn't make sense - those events are the result of show()/hide() calls (and the like), not the trigger. If you want to toggle the window visiblity by clicking the tray icon, try setVisible(!isVisible()) on the widget, if you want to hide the window when the user clicks the window close button try reimplementing closeEvent():
MyMainWindow::closeEvent( QCloseEvent* e ) {
hide();
e->accept();
}
In Python, that is
def closeEvent(self, event):
self.hide()
event.accept()

Categories

Resources