How can I share data with a pyside2 slot function? - python-3.x

I have a PySide2 GUI application with a QPushButton button with a #Slot function connected to it. How can I share data with the function?
from PySide2.QtCore import Slot
from PySide2.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QVBoxLayout
#Slot()
def button_XYZ_callback():
# Function which is executed when the button XYZ is clicked.
# I'd like to access the __main__s context data "parent_data" here.
pass
if __name__ == '__main__':
# parent context data what I want to access (read only)
parent_data = "blub"
application = QApplication(sys.argv)
window = QMainWindow()
central_widget = QWidget()
xyz_button = QPushButton("XYZ", central_widget)
xyz_button.clicked.connect(button_xyz_callback)
layout = QVBoxLayout(central_widget)
layout.addWidget(xyz_button)
window.show()
sys.exit(application.exec_())

Per Python's LEGB rule, the global variable parent_data is accessible from within the button_XYZ_callback function.
If, however, you wish to reduce the function's dependence on global variables, the standard technique is to define a class, and use class or instance attributes to store what was before global values:
# based on code from https://wiki.qt.io/Qt_for_Python_Tutorial_ClickableButton
import sys
from PySide2 import QtCore, QtWidgets, QtGui
class MyWidget(QtWidgets.QWidget):
def __init__(self, data):
QtWidgets.QWidget.__init__(self)
self.data = data
self.button = QtWidgets.QPushButton("Click me!")
self.text = QtWidgets.QLabel("Hello World")
self.text.setAlignment(QtCore.Qt.AlignCenter)
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.text)
self.layout.addWidget(self.button)
self.setLayout(self.layout)
self.button.clicked.connect(self.button_XYZ_callback)
#QtCore.Slot()
def button_XYZ_callback(self):
print(self.data)
if __name__ == "__main__":
parent_data = "blub"
app = QtWidgets.QApplication(sys.argv)
widget = MyWidget(data=parent_data)
widget.show()
sys.exit(app.exec_())
Alternatively, if the data is known before the callback is to be defined, you could use a function factory to place the data in the enclosing scope of the callback:
import sys
from PySide2.QtCore import Slot
from PySide2.QtWidgets import QApplication, QPushButton
def make_callback(data):
#Slot()
def button_XYZ_callback():
print(data)
return button_XYZ_callback
if __name__ == "__main__":
parent_data = "blub"
# https://wiki.qt.io/Qt_for_Python_Tutorial_ClickableButton
app = QApplication(sys.argv)
button = QPushButton("Click me")
button.clicked.connect(make_callback(parent_data))
button.show()
app.exec_()

Related

How to use a pyqtsignal to transmit a window which retains its function?

I use a pyqt_signal to transmit a sub window, which has a button whose function is to print. I use a thread to transmit this sub window to the main window to show, however the button loses its function. I know that I should put the statement self.sub_window = SubWindow() into the __init__ function in the second class, but how can I achieve the same effect if I still put this statement here.
# -*- coding: utf-8 -*-
from threading import currentThread
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import sys
from PyQt5.QtCore import pyqtSignal, QObject, QThread
class SubWindow(QWidget):
def __init__(self):
super(SubWindow, self).__init__()
self.resize(400, 400)
self.button = QPushButton(self)
self.button.setText('push me to print ***')
self.button.move(200, 200)
self.button.clicked.connect(self.print_)
def print_(self):
print('***')
class SignalStore(QThread):
window_signal = pyqtSignal(object)
def __init__(self):
super(SignalStore, self).__init__()
def run(self):
# if i put this statement here, how can i acquire window's print button function
self.sub_window = SubWindow()
self.window_signal.emit(self.sub_window)
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(400, 400)
self.button = QPushButton(self)
self.button.setText('push me to get subwindow')
self.button.move(200, 200)
self.button.clicked.connect(self.send_signal)
self.med_signal = SignalStore()
self.med_signal.window_signal.connect(self.get_sub_window)
def send_signal(self):
self.med_signal.start()
def get_sub_window(self, para):
self.sub_window = para
self.sub_window.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
Don't create or access gui objects inside threads. Read Qt guide.
GUI Thread and Worker Thread
As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.
This is probably what you are looking for:
import time
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import sys
from PyQt5.QtCore import pyqtSignal, QThread, pyqtSlot
class SubWindow(QWidget):
def __init__(self, parent=None):
super(SubWindow, self).__init__(parent)
self.resize(400, 400)
self.button = QPushButton(self)
self.button.setText('push me to print ***')
self.button.move(200, 200)
self.button.clicked.connect(self.print_)
#pyqtSlot()
def print_(self):
print('hello from subwindow')
class SignalStore(QThread):
print_func = pyqtSignal(str)
def __init__(self):
super(SignalStore, self).__init__()
def run(self):
time.sleep(1) # fake working...
self.print_func.emit("hello from thread")
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(400, 400)
self.subwin = SubWindow()
self.button = QPushButton(self)
self.button.setText('push me to get subwindow')
self.button.move(200, 200)
self.button.clicked.connect(self.send_signal)
self.med_signal = SignalStore()
self.med_signal.print_func.connect(self.print_from_main)
def send_signal(self):
self.subwin.show()
self.med_signal.start()
#pyqtSlot(str)
def print_from_main(self, string: str):
print(string)
self.subwin.print_()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())

Issue in Pyqt with next and previous Pages

so I'm currently working on a little project but I have an issue and all what I've tried did not work. I have 2 files :
Page1test :
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QPushButton, QWidget, QLabel
import sys
from page2test import Page2
class Page1(QWidget):
def __init__(self):
super(Page1, self).__init__()
self.setWindowTitle("Page 1")
label1 = QLabel(self)
label1.setText("\n PAGE 1")
self.btn_inMyApp = QPushButton ('Next page', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage1_OpenPage2)
self.show()
def closePage1_OpenPage2(self):
self.Open = Page2()
self.Open.showMaximized()
self.close()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Page1()
window.showMaximized()
sys.exit(app.exec_())
And page2test :
from PyQt5.QtWidgets import QPushButton, QWidget, QLabel
from Page1test import Page1
class Page2(QWidget):
def __init__(self):
super(Page2, self).__init__()
self.setWindowTitle("Test window principale")
label1 = QLabel(self)
label1.setText("\n Page2")
self.btn_inMyApp = QPushButton ('previous Page', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage2_OpenPage1)
self.show()
def closePage2_OpenPage1(self):
self.Open = Page1()
self.Open.showMaximized()
self.close()
I run the code of Page1test : empty window with just a Qpushbutton "Next Page", goal : we click on it and it open Page 2 (and close Page 1). And, when we are in Page 2, we have A Qpushbutton with "Previous Page" and when we click on it, it opens page 1, and close Page 2. Like a loop.
But, when I run the code, it returns an error :
cannot import name 'Page2' from partially initialized module 'page2test' (most likely due to a circular import)
and I have no idea how to fix it...
If someone had an idea, it would be really helpful.
So, I've finally found the solution, that was not so difficult in the end. Here it is (if it can help someone) :
Instead of making 2 files, I've done just 1 file. Here is the code :
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QPushButton, QWidget, QLabel, QMainWindow
import sys
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setWindowTitle("Page 1")
label1 = QLabel(self)
label1.setText("\n PAGE 1")
self.btn_inMyApp = QPushButton ('Page suivante', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage1_OpenPage2)
self.show()
def btn1_onClicked(self):
pass
def closePage1_OpenPage2(self):
self.Open = NewApp()
self.Open.showMaximized()
self.close()
class NewApp(QMainWindow):
def __init__(self):
super(NewApp, self).__init__()
self.setWindowTitle("Test window principale")
label1 = QLabel(self)
label1.setText("\n Page2")
self.btn_inMyApp = QPushButton ('previous Page', self)
self.btn_inMyApp.setGeometry(1500,800,275,125)
self.btn_inMyApp.clicked.connect(self.closePage2_OpenPage1)
self.show()
def closePage2_OpenPage1(self):
self.Open = MyApp()
self.Open.showMaximized()
self.close()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MyApp()
window.showMaximized()
sys.exit(app.exec_())
```

Creating new window using keyboard keys [duplicate]

I am programming a simple GUI, that will open a opencv window at a specific point. This window has some very basic keyEvents to control it. I want to advance this with a few functions. Since my QtGui is my Controller, I thought doing it with the KeyPressedEvent is a good way. My Problem is, that I cannot fire the KeyEvent, if I am active on the opencv window.
So How do I fire the KeyEvent, if my Gui is out of Focus?
Do I really need to use GrabKeyboard?
The following code reproduces my Problem:
import sys
from PyQt5.QtWidgets import (QApplication, QWidget)
from PyQt5.Qt import Qt
import cv2
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.first = True
def openselect(self):
im = cv2.imread(str('.\\images\\Steine\\0a5c8e512e.jpg'))
self.r = cv2.selectROI("Image", im)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Space and self.first:
self.openselect()
self.first = False
print('Key Pressed!')
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
The keyPressEvent method is only invoked if the widget has the focus so if the focus has another application then it will not be notified, so if you want to detect keyboard events then you must handle the OS libraries, but in python they already exist libraries that report those changes as pyinput(python -m pip install pyinput):
import sys
from PyQt5 import QtCore, QtWidgets
from pynput.keyboard import Key, Listener, KeyCode
class KeyMonitor(QtCore.QObject):
keyPressed = QtCore.pyqtSignal(KeyCode)
def __init__(self, parent=None):
super().__init__(parent)
self.listener = Listener(on_release=self.on_release)
def on_release(self, key):
self.keyPressed.emit(key)
def stop_monitoring(self):
self.listener.stop()
def start_monitoring(self):
self.listener.start()
class MainWindow(QtWidgets.QWidget):
pass
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
monitor = KeyMonitor()
monitor.keyPressed.connect(print)
monitor.start_monitoring()
window = MainWindow()
window.show()
sys.exit(app.exec_())

PyQt5: QLineEdit doesn't show inside a QGroupBox

I'm creating a PyQt5 application in which I want to put some of QLineEdit widgets inside a QGroupBox.
When I run my application, GroupBox is visible and LineEdit is not.
In CreateLinesEdit() I commented a line which sets the line edit visible, but it opens lineEdit in a new window.
from PyQt5.QtWidgets import QGroupBox, QApplication, QLineEdit, QVBoxLayout, QWidget, QHBoxLayout
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
self.InitWindow()
def InitWindow(self):
self.BoxLayout()
self.AddBox()
self.CreateLinesEdit()
self.show()
def BoxLayout(self):
self.groupBoxScreen = QGroupBox()
self.vbox = QVBoxLayout()
self.vbox_screenGame = QVBoxLayout()
self.hbox_lineEdit = QHBoxLayout()
def AddBox(self):
self.vbox_screenGame.addItem(self.hbox_lineEdit)
self.groupBoxScreen.setLayout(self.vbox_screenGame)
self.vbox.addWidget(self.groupBoxScreen)
self.setLayout(self.vbox)
def CreateLinesEdit(self):
self.lines_edit = []
for i in range(0, 4):
LineEdit = QLineEdit()
# LineEdit.setVisible(True)
self.lines_edit.append(LineEdit)
self.hbox_lineEdit.addWidget(self.lines_edit[i])
if __name__ == "__main__":
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())

populating combo box with folders on disk using QFileSystemModel

Hi I have written this basic code trying to populate folders underneath the /Users/ directory, but I don't know what I am missing its not populating.
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class MyWindow(QtGui.QWidget):
"""docstring for MyWindow"""
def __init__(self, parent=None):
super(MyWindow, self).__init__()
self.setup()
def setup(self):
fsm = QtGui.QFileSystemModel()
fsm.setRootPath("/Users/")
layout = QtGui.QVBoxLayout()
combo = QtGui.QComboBox()
combo.setModel(fsm)
layout.addWidget(combo)
self.setLayout(layout)
def main():
app = QtGui.QApplication(sys.argv)
win = MyWindow()
win.show()
win.raise_()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I am getting a / in the comobobox instead of the whole list of folders under /Users/ directory.
I think its better to use QFileSystemModel instead of using os.listdir interms of efficiency and will update the view if somebody updates folder or adds folder in the /Users/ directory !
Remember that QFileSystemModel is a hierarchical model, so you need to let the QComboBox know which QModelIndex represents the children you want to display. You do that with QComboBox.setRootModelIndex()
QFileSystemModel.setRootPath() conveniently returns the QModelIndex of the path you set.
So a small change is all you need (tested on Windows) -
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class MyWindow(QtGui.QWidget):
"""docstring for MyWindow"""
def __init__(self, parent=None):
super(MyWindow, self).__init__()
self.setup()
def setup(self):
fsm = QtGui.QFileSystemModel()
index = fsm.setRootPath("/Users/")
layout = QtGui.QVBoxLayout()
combo = QtGui.QComboBox()
combo.setModel(fsm)
combo.setRootModelIndex(index)
layout.addWidget(combo)
self.setLayout(layout)
def main():
app = QtGui.QApplication(sys.argv)
win = MyWindow()
win.show()
win.raise_()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Resources