Problem with the KeypressEvent of a TableView - python

I have a form with a TableView and a function that handles refreshing buttons for adding and removing data in the table etc.
I want to update the states of actions in contextmenu:
I can with the mouse events (tableWidget.clicked) but not with the KeyPressEvent (Up and Down)
I tried with the table keyPressEvent event, but for some reason the keys no longer work.
How to intercept the up and down keys of the table to update buttons for example.
here after an extract of the code :
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QModelIndex, QVariant, QAbstractItemModel, QEvent
from PyQt5.QtGui import QFont, QKeyEvent
import numpy as np
from modules import *
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setObjectName("MainWindow")
self.setMinimumSize(QtCore.QSize(1024, 860))
self.setMaximumSize(QtCore.QSize(2400, 1024))
self.setStyleSheet("background-color: rgb(240, 240, 240);")
self.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
self.centralWidget = QtWidgets.QWidget(self)
self.gridLayout = QtWidgets.QGridLayout(self.centralWidget)
self.setCentralWidget(self.centralWidget)
self.retranslateUi()
self.connectActions()
self.tableWidget.customContextMenuRequested.connect(self.context_menu_Show)
def bpuHadChanged(self, changed=False):
self.bpuChanged = changed
self.actionSave.setEnabled(changed)
def saveFile(self):
# TODO
self.actionSave.setEnabled(False)
def setBPU(self, bpu):
self.bpu = bpu
self.model = TableModel(self.bpu, bpuNewLine)
self.tableWidget.setModel(self.model)
self.tableWidget.resizeColumnToContents(0)
self.updateActionsStates()
def connectActions(self):
self.actionNew.triggered.connect(self.newFile)
self.actionQuit.triggered.connect(self.quit)
self.actionAdd_une_ligne_bordereau.triggered.connect(self.addLine)
self.actionRemoveSelectedLine.triggered.connect(self.RemoveLigne)
self.actionInsertLine.triggered.connect(self.insertLine)
self.actionSave.triggered.connect(self.saveFile)
self.tableWidget.clicked.connect(self.updateActionsStates)
# TODO add connections for up et down actions
def context_menu_Show(self):
self.updateActionsStates()
cursor = QtGui.QCursor()
self.contextMenu.exec_(cursor.pos())
pass
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.setBPU(bpu)
window.show()
app.exec_()

Related

pyqt5 : Build an overlay that updates under specific conditions on top of an existing program

(pyqt5 noob here.)
I want to create an overlay that change text location when some conditions are met. I run it under a thread, that I can control from main, as this would be used as an extension of an already existing program. This one would provide coordinates, and the overlay should update the text location based on those.
However, this error is triggered :
A QApplication::exec: Must be called from the main thread is called
I don't get what I'm missing here.
(I've noticed that using X11BypassWindowManagerHint causes some issues, especially with event triggers, but I want the overlay to be transparent, always displayed above the window I'm using, and clickable-through).
Any help, advice or suggestions would be much appreciated !
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QWidget, QDesktopWidget
from PyQt5.QtGui import QFont
from PyQt5.QtCore import Qt, QCoreApplication, QThread, pyqtSignal, pyqtSlot
from time import sleep
class text_thread(QThread):
position_changed = pyqtSignal(int, int)
def __init__(self, app):
self.app = app
QThread.__init__(self)
def run(self):
self.window = QMainWindow()
self.window.setWindowFlag(Qt.X11BypassWindowManagerHint)
self.window.setWindowFlag(Qt.FramelessWindowHint)
self.window.setWindowFlag(Qt.WindowStaysOnTopHint)
self.window.setAttribute(Qt.WA_TranslucentBackground)
self.window.resize(500, 500)
self.f = QFont("Arial", 30, QFont.Bold)
self.label = QLabel("Text")
self.label.setFont(self.f)
self.label.setGeometry(100, 100, 50, 50)
self.layout = QVBoxLayout()
self.layout.addWidget(self.label)
self.central_widget = QWidget()
self.central_widget.setLayout(self.layout)
self.window.setCentralWidget(self.central_widget)
self.position_changed.connect(self.update_position)
self.window.show()
self.app.exec()
#pyqtSlot(int, int)
def update_position(self, x, y):
self.label.setGeometry(x, y, 50, 50)
app = QApplication(sys.argv)
thread = text_thread(app)
thread.start()
while True:
for i in range(10):
thread.position_changed.emit(100+i*10, 100+i*10)
sleep(1)
I believe that this way you can get a result similar to what you want.
I recommend isolating your test tool.
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QWidget, QDesktopWidget
from PyQt5.QtGui import QFont
from PyQt5.QtCore import pyqtSignal as Signal, QObject, Qt, QCoreApplication, QThread, pyqtSignal, pyqtSlot
import time
class Tester(QObject):
sig_positions = Signal(int, int)
#pyqtSlot(int)
def action(self, n):
for i in range(1, n+1):
time.sleep(1)
self.sig_positions.emit(100+i*10, 100+i*10)
class Window(QMainWindow):
sig_requested = Signal(int)
def __init__(self):
super().__init__()
self.setWindowFlag(Qt.X11BypassWindowManagerHint)
self.setWindowFlag(Qt.FramelessWindowHint)
self.setWindowFlag(Qt.WindowStaysOnTopHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.resize(500, 500)
self.f = QFont("Arial", 30, QFont.Bold)
self.label = QLabel("Text")
self.label.setFont(self.f)
self.label.setGeometry(100, 100, 50, 50)
self.layout = QVBoxLayout()
self.layout.addWidget(self.label)
self.central_widget = QWidget()
self.central_widget.setLayout(self.layout)
self.setCentralWidget(self.central_widget)
self.t = Tester()
self.q = QThread()
self.t.sig_positions.connect(self.update_position)
self.sig_requested.connect(self.t.action)
self.t.moveToThread(self.q)
self.q.start()
self.show()
def start_test(self):
print('start')
self.sig_requested.emit(3)
def update_position(self, x, y):
print('update_position')
self.label.setGeometry(x, y, 50, 50)
if __name__ == "__main__":
App = QApplication(sys.argv)
window = Window()
window.start_test()
sys.exit(App.exec())

PYQT5 GIF freezes on GUI initialization

Keep gif running while GUI starts. Is that possible? I have read many reporitys but none with the true and understandable answer.
I have prepared a code example that shows the problem.
import sys
from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget
from PyQt5 import QtWidgets
from PyQt5.QtGui import QMovie
from PyQt5.QtCore import QSize, QThread
class Main_Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(500, 500))
self.setWindowTitle("Main Window")
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
gridLayout = QGridLayout(self)
centralWidget.setLayout(gridLayout)
gif = QLabel(self)
gif.setGeometry(0,0,500,500)
self.movie = QMovie(r"C:\Users\...\Pictures\Icon_LOAD.gif")
gif.setMovie(self.movie)
self.movie.start()
# #Call event handler to process the queue, but it is shocking, dirty and unsuitable
#app.processEvents()
self.show()
for i in range(0,1000000):
print(i)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWin = Main_Window()
sys.exit(app.exec_())
Now it works smooth
import sys
from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget
from PyQt5 import QtWidgets
from PyQt5.QtGui import QMovie
from PyQt5.QtCore import QSize
from threading import Thread
class Main_Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(500, 500))
self.setWindowTitle("Main Window")
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
gridLayout = QGridLayout(self)
centralWidget.setLayout(gridLayout)
gif = QLabel(self)
gif.setGeometry(0,0,500,500)
self.movie = QMovie(r"gif.gif")
gif.setMovie(self.movie)
self.movie.start()
self.show()
Thread(target=self.function_on_thread).start()
def function_on_thread(self):
for i in range(0,1000000):
print(i)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWin = Main_Window()
sys.exit(app.exec_())

Python PyQt5 Create Layout

I'm trying to create a layout with pyqt5, but i cannot do it. There are so many error. Why i am getting so many errors.
autoFillBackground() takes no arguments, and MainWindow will not assume the palette of it's central widget unless you explicitly set it. Also, this has nothing to do with a layout.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Color(QWidget):
def __init__(self, color):
super().__init__()
self.autoFillBackground()
palette = self.palette()
palette.setColor(QPalette.Window, QColor(color))
self.setPalette(palette)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
widget = Color('Blue')
self.setCentralWidget(widget)
self.setPalette(widget.palette())
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())

Open a file from main window to a new window in PyQt5 (in different files)

I have two files, one for my main window, which has one image and one button and one for a new window. What I want it to do is that when I push the button from my main window, it lets me load a file and show it in a TextEdit widget in the new window
so here I have the files I'm using:
MainWindow.py
import sys
import os
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QTextEdit, QHBoxLayout, QLabel, QMainWindow, QAction, QFileDialog
class Window(QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.img = QLabel()
self.relleno=QLabel()
self.btn_load = QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
h_layout = QHBoxLayout()
v_layout = QVBoxLayout()
h_final = QHBoxLayout()
h_layout.addWidget(self.img)
v_layout.addWidget(self.btn_load)
h_final.addLayout(h_layout)
h_final.addLayout(v_layout)
self.btn_load.clicked.connect(self.loadafile)
self.setLayout(h_final)
self.setWindowTitle('This is main window')
self.setGeometry(600,150,self.width,self.height)
self.show()
def loadafile(self):
filename = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
with open(filename[0], 'r') as f:
file_text = f.read()
return file_text
def main():
app = QApplication(sys.argv)
main = Window()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
NewWindow.py
import os
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QTextEdit, QVBoxLayout
from MainWindow import loadafile
info=loadafile()
class SecondWindow(QWidget):
def __init__(self):
super(SecondWindow, self).__init__()
self.text = QTextEdit(self)
self.init_ui()
def init_ui(self):
v_layout = QVBoxLayout()
v_layout.addWidget(self.text)
self.setLayout(v_layout)
self.setText(info)
self.setWindowTitle('Opened Text')
self.show()
app = QApplication(sys.argv)
shower = SecondWindow()
sys.exit(app.exec_())
I think the loadafile does return my file_text variable but I don't know how to open the new window from there. I think I need to use a destructor for main window and then show the new window but I'm not sure of how to do this (This is the first time I try OOP)
A program is not a set of files, especially in OOP a program is the interactions of objects. And the objects interact if they have the same scope, so both windows must be created in one place so that the information from one pass to the other.
On the other hand in Qt there is a fundamental concept that is the signals, this functionality allows to notify the change of a state to another object without a lot of dependency, so in this case I will create a signal that transmits the text to the other object.
NewWindow.py
from PyQt5 import QtWidgets
class SecondWindow(QtWidgets.QWidget):
def __init__(self):
super(SecondWindow, self).__init__()
self.text = QtWidgets.QTextEdit(self)
self.init_ui()
def init_ui(self):
v_layout = QtWidgets.QVBoxLayout(self)
v_layout.addWidget(self.text)
self.setWindowTitle('Opened Text')
self.show()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
shower = SecondWindow()
sys.exit(app.exec_())
MainWindow.py
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from NewWindow import SecondWindow
class Window(QtWidgets.QWidget):
textChanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.img =QtWidgets.QLabel()
self.relleno=QtWidgets.QLabel()
self.btn_load = QtWidgets.QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
h_final = QtWidgets.QHBoxLayout(self)
h_final.addWidget(self.img)
h_final.addWidget(self.btn_load)
self.btn_load.clicked.connect(self.loadafile)
self.setWindowTitle('This is main window')
self.setGeometry(600,150,self.width,self.height)
self.show()
#QtCore.pyqtSlot()
def loadafile(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
with open(filename, 'r') as f:
file_text = f.read()
self.textChanged.emit(file_text)
def main():
app = QtWidgets.QApplication(sys.argv)
main = Window()
s = SecondWindow()
main.textChanged.connect(s.text.append)
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Qt Multiple GraphicsEffects

Is there a way in Qt5 to apply a QGraphicsEffect to a widget, even when one of its parent widgets already has a QGraphicsEffect applied to it?
When i try the following:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class ParentWidget(QWidget):
def __init__(self,parent):
super().__init__(parent)
effect = QGraphicsBlurEffect(self)
self.setGraphicsEffect(effect)
effect.setBlurRadius(0)
class ChildWidget(ParentWidget):
def __init__(self,parent):
super().__init__(parent)
self.layout = QGridLayout(self)
widget = QWidget()
widget.setObjectName('reviewControlArea')
effect = QGraphicsOpacityEffect(widget)
widget.setGraphicsEffect(effect)
self.layout.addWidget(widget)
if __name__ == "__main__":
app = QApplication(sys.argv)
MainWindow = QMainWindow()
cw = ChildWidget(MainWindow)
MainWindow.setCentralWidget(cw)
MainWindow.show()
sys.exit(app.exec_())
The stdout says:
QPainter::begin: A paint device can only be painted by one painter at a time.

Categories