I have a QTableView with a verticalScrollBar()
The problem is that when I use the mouse wheel to scroll down/up it moves 3 rows at a time, I'd like to change this to only move 1 row at a time.
Looking at the class reference it seems to implement QWheelEvent,so my best guess is I must overwrite this event...however I have absolutely no idea where to start.
Any help would be much appreciated!
I'm using Python 3.10.5 and Pyqt 6.3 on Arch Linux (Manjaro)
The QApplication class has the setWheelScrollLines method for setting this property globally. But if you only want it to affect one widget, you can create a subclass and utilise it in a reimplemented wheelEvent.
Here's a simple demo:
from PyQt6 import QtGui, QtWidgets
class TableView(QtWidgets.QTableView):
def __init__(self):
super().__init__()
model = QtGui.QStandardItemModel(self)
for row in range(100):
model.appendRow(QtGui.QStandardItem())
self.setModel(model)
def wheelEvent(self, event):
lines = QtWidgets.QApplication.wheelScrollLines()
try:
QtWidgets.QApplication.setWheelScrollLines(1)
super().wheelEvent(event)
finally:
QtWidgets.QApplication.setWheelScrollLines(lines)
app = QtWidgets.QApplication(['Test'])
window = TableView()
window.show()
app.exec()
Related
I've been using PyQt and PySide for a while. Today I stumbled upon a weird behaviour : re-implementing paintEvent does not seem to work in Python versions of Qt5. I never had this problem in Qt4.
from PySide2 import QtWidgets, QtCore, QtGui # use pyside
# from PyQt5 import QtWidgets, QtCore, QtGui # use pyqt
import sys
class TagWidget(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__(parent)
print("__init__")
def paintEvent(self, e):
# this is called or not
# depends (see below)
print("paintEvent")
raise(AssertionError)
class MyGui(QtWidgets.QMainWindow):
def __init__(self,parent=None):
super(MyGui, self).__init__()
self.setupUi()
def setupUi(self):
self.setGeometry(QtCore.QRect(100,100,500,500))
self.w=QtWidgets.QWidget(self)
self.setCentralWidget(self.w)
self.lay = QtWidgets.QHBoxLayout(self.w)
self.image = TagWidget(self.w)
self.lay.addWidget(self.image)
# return
# exit here, and TagWidget.paintEvent
# is still being called
self.file_list = QtWidgets.QListWidget(self.w)
# return
# exit here, and TagWidget.paintEvent
# is still being called
self.lay.addWidget(self.file_list)
# .. but if we reach all the way here,
# TagWidget.paintEvent is never called !
def main():
app=QtWidgets.QApplication(["test_app"])
mg=MyGui()
mg.show()
app.exec_()
if (__name__=="__main__"):
main()
So, we're just testing if paintEvent is being called (by raising AssertionError when it's called).
Once we add another widget to the same layout where TagWidget sits, the paintEvent is not effective anymore.
So weird. Help appreciated.
paintEvent() is called when it is necessary to repaint, if the widget has size(0, 0), or size invalid or is hidden that method is not called, and that is what happens in your case, when using a layout it will take the size of sizeHint() by default, by default a QWidget sizeHint() is QSize(-1, -1) and therefore no need to paint.
So the solution is to set an appropriate sizeHint():
class TagWidget(QtWidgets.QWidget):
def paintEvent(self, e):
print("paintEvent")
raise(AssertionError)
def sizeHint(self):
print("default sizeHint: ", super(TagWidget, self).sizeHint())
return QtCore.QSize(640, 480)
I've tried it with PyQt4 and PySide and the same problem happens, so the problem is not Qt but the example in particular.
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)
I'm making a large program in Python and using PyQt for the GUI. The whole program is divided into different modules so that different people can work on it simultaneously without interfering with the other people's work.
I am working on 3 different modules right now. 1 is the main program window that handles the basic UI and assigns widgets so the main window (this is not important, just so you know why the code doesn't look like a full program.)
First is the widget:
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from CustomButton import HoverButton #just a custom button class
from CustomGif import LblLoadingGif #where things go wrong
class Page1(QtGui.QWidget):
def __init__(self, parent=None):
super(Page1, self).__init__(parent)
self.lbl1GIF = LblLoadingGif(self)
self.lbl1GIF.move(400, 45)
self.btnStart = HoverButton(self)
self.btnStart.setText('Start')
self.btnStart.move(35, 400)
self.btnStart.clicked.connect(self.actStartGif)
#the code below works, but then I can only perform 1 action with each button
#self.btnStart.clicked.connect(self.lbl1GIF.actStart)
def actStartGif(self):
self.lbl1GIF.actStart
The code for the custom GIF looks as follows:
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
class LblLoadingGif(QtGui.QLabel):
def __init__(self, parent=None):
QtGui.QLabel.__init__(self, parent)
self.setStyleSheet('background: url();')
self.setScaledContents(True)
self.resize(100, 100)
self.movLoadGif = QtGui.QMovie('Resources_Images/Loading6.gif', QtCore.QByteArray())
self.movLoadGif.setCacheMode(QtGui.QMovie.CacheAll)
self.movLoadGif.setSpeed(100)
self.setMovie(self.movLoadGif)
self.hide()
def actStart(self, event):
#print('test1')
self.show()
self.movLoadGif.start()
def actStop(self, event):
#print('test1')
self.hide()
self.movLoadGif.stop()
So the problem is that I can use the actStart function just fine when I call it from the button click directly, but not when I call it through another function. I have used a lot of different variations of brackets, self, Page1 when calling the actStart of the custom gif from withing the actStartGif function.
Any help will be appreciated.
When you use connect it is necessary to pass the name of the function since internally it is in charge of calling it, in your case you have to call it directly so you will have to pass its parameters, in this case event:
self.lbl1GIF.actStart({your value for event})
I do not understand why you use event for what happens to you None:
def actStartGif(self):
self.lbl1GIF.actStart(None)
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
Okay... This has been bugging me for hours. I have a qtmainwindow with a menubar. I've managed to connect an action in tje menubar to an independent Qwidget. But as soon as the Qwidget appears it disappears. I'm using the latest version of pyqt.
Here's the code:
Import sys
from PyQt4 import QtGui, QtCore
Class Main(QtGui.QtMainWindow) :
def __init__(self) :
QtGui.QtMainWindow.__init__(self)
self.setGeometry(300,300,240,320)
self.show()
menubar = self. menuBar()
filemenu = menubar. addMenu('&File')
new = QtGui.QAction(QtGui.QIcon('new.png'), 'New', self)
new.triggered.connect(self.pop)
filemenu.addAction(new)
def pop(self) :
pop = Pop()
class Pop(QtGui.QWidget) :
def __init__(self) :
QtGui.QWidget.__init__(self)
self.setGeometry(300,300,240,320>
self.setWindowTitle('Pop up')
self.show()
Update the pop(self) method as:
def pop(self):
self.window = Pop()
you need to store object of newly created window in a member variable, other wise as soon as the method finishes with the execution, local variables will be destroyed by the Python Garbage Collector.
if you implement this code, you will see the window gets created and disappears immediately.
import sys
from PyQt5 import QtGui, QtWidgets,QtCore
from PyQt5.QtWidgets import QApplication, QMainWindow
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QWidget()
window.setGeometry(50,50,500,500)
window.setWindowTitle("GUI window")
window.show()
To solve that problem write "sys.exit(app.exec_())" after window.show() and the window will stay on the screen.