PyQt5:How to communicate between two subwindows? - python

How to update the combobox in subwindow1 from subwindow2.The following code should be working fine but it doesnot update the combobox and doesnot show any errors at the line
AddressBook().updatestock(name)
in the add function.It looks like it creates an instance of the AddressBook.If so how can i rectify it.
import os
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class menudemo(QMainWindow):
def __init__(self, parent=None):
super(menudemo, self).__init__(parent)
self.pilot_widget=AddInventory()
self.drop_widget=AddressBook()
self.centralWidget=QMdiArea(self)
self.setCentralWidget(self.centralWidget)
self.sub1=QMdiSubWindow()
self.sub1.setWidget(self.drop_widget)
self.centralWidget.addSubWindow(self.sub1)
self.sub1.show()
self.sub2=QMdiSubWindow()
self.sub2.setWidget(self.pilot_widget)
self.centralWidget.addSubWindow(self.sub2)
self.sub2.show()
self.setWindowTitle("menu demo")
self.showMaximized()
class AddInventory(QWidget):
def __init__(self, parent=None):
super(AddInventory, self).__init__(parent)
self.statement=""
self.nameLabel = QLabel("Item:")
self.name=QLineEdit()
self.addButton = QPushButton("&Add")
self.addButton.setDefault(True)
self.addButton.clicked.connect(self.add)
mainLayout = QGridLayout()
mainLayout.addWidget(self.nameLabel, 0, 0)
mainLayout.addWidget(self.name,0,1)
mainLayout.addWidget(self.addButton, 0, 3)
self.setLayout(mainLayout)
self.setWindowTitle(" Address Book")
self.setFixedSize(self.sizeHint())
def add(self,text):
name=self.name.text()
AddressBook().updatestock(name)
self.name.clear()
class AddressBook(QWidget):
def __init__(self, parent=None):
super(AddressBook, self).__init__(parent)
nameLabel2 = QLabel("Item:")
self.itemstock=QComboBox(self)
self.itemstock.activated[str].connect(self.updatestock)
mainLayout = QGridLayout()
mainLayout.addWidget(nameLabel2, 0, 0)
mainLayout.addWidget(self.itemstock, 0, 1)
self.setLayout(mainLayout)
self.setWindowTitle("Simple Address Book")
self.setFixedSize(self.sizeHint())
def updatestock(self,name):
print(name)
self.itemstock.addItem(name)
def main():
app = QApplication(sys.argv)
ex = menudemo()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

It doesn't work, because you're just creating a new instance of AddressBook, you call a method on it.
It is not linked to self.drop_widget
You have to link both widgets using the constructor. You do this in the main
self.pilot_widget=AddInventory()
self.drop_widget=AddressBook()
I would do:
self.drop_widget=AddressBook()
self.pilot_widget=AddInventory(self.drop_widget)
and change AddInventory class parameters
class AddInventory(QWidget):
def __init__(self, drop_widget, parent=None):
self.drop_widget = drop_widget # now it is linked in your class as a member
now, in add:
def add(self,text):
name=self.name.text()
self.drop_widget.updatestock(name)
self.name.clear()

Related

Calling method from a second class to update QListView data

I have two tabs, Tab1, and Tab2. On Tab2, there is a button that when clicked, calls a method that updates the QListView data in the same tab. This works successfully.
When trying to call the same method from another class, it will not work. Below is a minimum reproducible example.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import (
QDesktopWidget,
QVBoxLayout,
QHBoxLayout,
QPushButton,
)
from PyQt5.QtCore import Qt
class App(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.setWindowTitle('App')
self.resize(1200, 800)
self.center()
self.window = MainWindow(self)
self.setCentralWidget(self.window)
self.show()
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent):
super(MainWindow, self).__init__(parent)
layout = QVBoxLayout(self)
# Initialize Tabs
tab_holder = QtWidgets.QTabWidget()
tab1 = Home()
tab2 = SecondTab()
tab_holder.addTab(tab1, "Tab1")
tab_holder.addTab(tab2, 'Tab2')
layout.addWidget(tab_holder)
class Home(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Home, self).__init__(parent)
lay = QVBoxLayout(self)
self.btn_login = QPushButton('Login')
self.btn_login.clicked.connect(self.login)
lay.addWidget(self.btn_login)
#QtCore.pyqtSlot()
def login(self):
print('Hello')
SecondTab.load_info()
class SecondTab(QtWidgets.QWidget):
def __init__(self, parent=None):
super(SecondTab, self).__init__(parent)
lay = QVBoxLayout(self)
# Choice Boxes
layout_choice_boxes = QHBoxLayout()
self.list_of_items = QtWidgets.QListView()
self.model_dist = QtGui.QStandardItemModel(self.list_of_items)
layout_choice_boxes.addWidget(self.list_of_items)
# Load data button.
self.loadData = QPushButton('Load Data')
self.loadData.clicked.connect(self.load_info)
# Add all components to main layout.
lay.addLayout(layout_choice_boxes)
lay.addWidget(self.loadData)
#QtCore.pyqtSlot()
def load_info(self):
for member in ['Item 1', 'Item 2', 'Item 3']:
item = QtGui.QStandardItem(member)
item.setCheckable(True)
item.setEditable(False)
check = Qt.Unchecked
item.setCheckState(check)
self.model_dist.appendRow(item)
self.list_of_items.setModel(self.model_dist)
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
The error is on the line in the class Home() where I try to call the method from the SecondTab() class: SecondTab.load_info()
The load_info() method uses self in the SecondTab class, so I tried passing in the class directly like this: SecondTab.load_info(SecondTab()), however, it did not work.
This is a problem about the basic OOP issues, you have to interact with the instances (the objects) and not the classes (the abstraction). So the solution is that the connection between the objects "tab1" and "tab2":
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent):
super(MainWindow, self).__init__(parent)
layout = QVBoxLayout(self)
# Initialize Tabs
tab_holder = QtWidgets.QTabWidget()
tab1 = Home()
tab2 = SecondTab()
tab_holder.addTab(tab1, "Tab1")
tab_holder.addTab(tab2, 'Tab2')
layout.addWidget(tab_holder)
tab1.btn_login.clicked.connect(tab2.load_info)
class Home(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Home, self).__init__(parent)
lay = QVBoxLayout(self)
self.btn_login = QPushButton('Login')
lay.addWidget(self.btn_login)

When one checkbox is checked, by pressing a button to print some texts from a LineEdit if another checkbox is also checked

import sys, os
import PyQt4
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Tab1Widget1(QWidget):
def __init__(self, parent=None):
super().__init__()
self.Tab1Widget1initUI()
self.bridge = Tab1Widget2()
def Tab1Widget1initUI(self):
self.setLayout(QGridLayout())
self.T1W1_checkbox = QCheckBox('checkbox1', self)
self.layout().addWidget(self.T1W1_checkbox, 1, 0)
def test(self):
print ('123')
def run(self):
if self.T1W1_checkbox.isChecked() == True:
self.test()
if self.bridge.T1W2_checkbox.isChecked() == True:
print (self.bridge.T1W2_le.text())
class Tab1Widget2(QWidget):
def __init__(self, parent=None):
super().__init__()
self.setLayout(QGridLayout())
self.T1W2_checkbox = QCheckBox('checkbox2', self)
self.layout().addWidget(self.T1W2_checkbox, 0, 0)
self.T1W2_le = QLineEdit()
self.layout().addWidget(self.T1W2_le, 0, 1)
class Tab1Layout(QWidget):
def __init__(self, parent=None):
super().__init__()
self.setLayout(QGridLayout())
self.group1 = Tab1Widget1(self)
scroll = QScrollArea(self)
scroll.setWidget(self.group1)
scroll.setWidgetResizable(True)
self.layout().addWidget(scroll, 0, 0)
self.group2 = Tab1Widget2(self)
self.layout().addWidget(self.group2, 1, 0)
self.btnRun = QPushButton('Run', self)
self.layout().addWidget(self.btnRun, 3, 0)
self.btnRun.clicked.connect(self.group1.run)
class Page1(QTabWidget):
def __init__(self, parent=None):
super().__init__()
self.tab1 = Tab1Layout()
self.addTab(self.tab1, "Tab1")
self.tab2 = QWidget()
self.tab3 = QWidget()
self.addTab(self.tab2, "Tab2")
self.addTab(self.tab3, "Tab3")
self.tab2_initUI()
self.tab3_initUI()
def tab2_initUI(self):
grid = QGridLayout()
self.tab2.setLayout(grid)
def tab3_initUI(self):
grid = QGridLayout()
self.tab3.setLayout(grid)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__()
self.setGeometry(450, 250, 800, 550)
self.startPage1()
def startPage1(self):
x = Page1(self)
self.setWindowTitle("Auto Benchmark")
self.setCentralWidget(x)
self.show()
def main():
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
If checkbox1 is checked and I press the run button, it will print 123. However, by pressing run button, I want checkbox2 to also print some texts entered in lineedit if the checkbox1 are also checked (i.e. it should print 123 first and then print 456).
I've looked up some similar types of questions, but none of that provides a proper answer. If anyone knows how to solve it, pls let me know thanks!!
The problem is that you are creating several Tab1Widget2, the first one you created in Tab1Layout, and that is the one you see, then you have created another one in Tab1Widget1, but not the time because you have not passed a parent, if you pass it to ** self ** as parent you will observe the following:
self.bridge = Tab1Widget2(self)
which is not what you want, so instead of creating a new you must pass the one that already exists, an option is to pass it through the constructor:
class Tab1Widget1(QWidget):
def __init__(self, bridge, parent=None): # Modify here
super().__init__(parent)
self.Tab1Widget1initUI()
self.bridge = bridge #Modify here
# ...
def test(self): print ('123')
def run(self):
if self.T1W1_checkbox.isChecked():
self.test()
if self.bridge.T1W2_checkbox.isChecked():
print (self.bridge.T1W2_le.text())
class Tab1Widget2(QWidget):
# ...
class Tab1Layout(QWidget):
def __init__(self, parent=None):
super().__init__()
self.setLayout(QGridLayout())
self.group2 = Tab1Widget2(self) # Modify here
self.group1 = Tab1Widget1(self.group2, self) # Modify here
scroll = QScrollArea(self)
scroll.setWidget(self.group1)
scroll.setWidgetResizable(True)
self.layout().addWidget(scroll, 0, 0)
# self.group2 = Tab1Widget2(self) # Modify here
self.layout().addWidget(self.group2, 1, 0)
# ...

Live Plotting with PyQtGraph in PyQt4 #2

First of all sorry for the lenght. I want to explain my problem as good as possible. I am quite new to Python and trying to make a plotting app using PyQtGraph embedded in PyQt4. Some days ago I got a really nice answer to my plotting problem, and my next step is to have two PyQtGraphs plot Widgets simoultaneously plotting in the same PyQt4's CentralWidget. By the same approach as in the link described, both plots work fine, but the GUI gets unresponsive. For overcoming this, I am aiming to use QThread, and for that, I need to have my plotting functions in different classes. But I am messing with my variables, and can't see how:
from PyQt4 import QtCore, QtGui
import pyqtgraph as pg
import random
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.central_widget = QtGui.QStackedWidget()
self.setCentralWidget(self.central_widget)
self.login_widget = LoginWidget(self)
self.login_widget.button.clicked.connect(Plots.plotter)
self.central_widget.addWidget(self.login_widget)
class LoginWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(LoginWidget, self).__init__(parent)
layout = QtGui.QHBoxLayout()
self.button = QtGui.QPushButton('Start Plotting')
layout.addWidget(self.button)
self.plot = pg.PlotWidget()
layout.addWidget(self.plot)
self.setLayout(layout)
class Plots(MainWindow):
def print(self):
print('hello World')
def plotter(self):
self.data = [0]
self.curve = self.login_widget.plot.getPlotItem().plot()
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updater)
self.timer.start(0)
def updater(self):
self.data.append(self.data[-1]+0.2*(0.5-random.random()))
self.curve.setData(self.data)
if __name__ == '__main__':
app = QtGui.QApplication([])
window = MainWindow()
window.show()
app.exec_()
That gives me an error at Plots.plotter:
self.data = [0]
AttributeError: 'bool' object has no attribute 'data'
If in the MainWindow class I substitute the 'button.connect' argument with Plots.print, it works fine. So I can see there is something wrong with the fact that I make an LoginWidget object in MainWindow, and then Plots inherits from MainWindow, calling that same LoginWidget object again.
I have tried a proposed solution where the father (MainWindow) does not access the methods of the children (Plots). But if, from Plots, I want to call the class where I aim placing my thread in, I get the same error again....
import sys
from PyQt4 import QtCore, QtGui
import pyqtgraph as pg
import random
class LoginWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(LoginWidget, self).__init__(parent)
layout = QtGui.QHBoxLayout()
self.button = QtGui.QPushButton('Start Plotting')
layout.addWidget(self.button)
self.plot = pg.PlotWidget()
layout.addWidget(self.plot)
self.setLayout(layout)
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.central_widget = QtGui.QStackedWidget()
self.setCentralWidget(self.central_widget)
self.login_widget = LoginWidget(self)
self.central_widget.addWidget(self.login_widget)
class Plots(MainWindow):
def __init__(self, parent=None):
super(Plots, self).__init__(parent=parent)
self.login_widget.button.clicked.connect(MyThread.plotter)
class MyThread(MainWindow):
def __init__(self, parent=None):
super(Aname, self).__init__(parent=parent)
def print(self):
print('hello World')
def plotter(self):
self.data = [0]
self.curve = self.login_widget.plot.getPlotItem().plot()
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updater)
self.timer.start(0)
def updater(self):
self.data.append(self.data[-1]+0.2*(0.5-random.random()))
self.curve.setData(self.data)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = Plots()
w.show()
sys.exit(app.exec_())
In inheritance the father should not access the methods of the children, It is better to inherit and implement new methods that have nothing to do with the parent in the child class.
Timer Version
import random
import sys
import pyqtgraph as pg
from PyQt4 import QtGui, QtCore
class LoginWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(LoginWidget, self).__init__(parent)
layout = QtGui.QHBoxLayout()
self.button = QtGui.QPushButton('Start Plotting')
layout.addWidget(self.button)
self.plot = pg.PlotWidget()
layout.addWidget(self.plot)
self.setLayout(layout)
self.button.clicked.connect(self.plotter)
def plotter(self):
self.data = [0]
self.curve = self.plot.getPlotItem().plot()
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updater)
self.timer.start(0)
def updater(self):
self.data.append(self.data[-1] + 0.2 * (0.5 - random.random()))
self.curve.setData(self.data)
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.centralwidget = QtGui.QWidget(self)
self.setCentralWidget(self.centralwidget)
self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget)
self.login_widget_1 = LoginWidget(self)
self.horizontalLayout.addWidget(self.login_widget_1)
self.login_widget_2 = LoginWidget(self)
self.horizontalLayout.addWidget(self.login_widget_2)
self.setCentralWidget(self.centralwidget)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

PyQt signal slot issue with signal outside creating class

I am trying to create a toolbar that can be modified to change actions on the fly.
However signals are not being sent when I add actions from outside the class that creates the toolbar.
In the example below the new action is never triggered. Any idea on how this can be done?
import sys
from PyQt4 import QtGui
from toolbarmodifier import ToolbarModifier
class FluidToolbar(QtGui.QMainWindow):
def __init__(self):
super(FluidToolbar, self).__init__()
self.initUI()
def initUI(self):
createAction = QtGui.QAction( 'create Action', self)
createAction.triggered.connect(self.createActions)
self.toolbar = self.addToolBar('create Action')
self.toolbar.addAction(createAction)
self.setGeometry(300, 300, 300, 200)
self.show()
def createActions(self):
print(">>createActions()")
toolbarModifier = ToolbarModifier()
toolbarModifier.addAction(self)
def main():
app = QtGui.QApplication(sys.argv)
ex = FluidToolbar()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
toolbarmodifier.py
from PyQt4 import QtGui
from PyQt4.QtGui import QWidget
class ToolbarModifier(QWidget):
def __init__(self):
super(ToolbarModifier, self).__init__()
def newActionTriggered(self):
print(">>newActionTriggered()")
def addAction(self, gui):
triggerAction = QtGui.QAction( 'New action', gui)
triggerAction.triggered.connect(self.newActionTriggered)
gui.toolbar.addAction(triggerAction)
print("<<addAction()")
Not having a link back to parent was the issue. In FluidToobar modify code in createActions method to include self in call:
toolbarModifier = ToolbarModifier(self)
In ToolbarModifier change first few lines to:
class ToolbarModifier(QtCore.QObject):
def __init__(self, parent=None):
super(ToolbarModifier, self).__init__(parent)

How to add a Status to this PySide based ui

Look at the code
import sys
from PySide import QtCore, QtGui
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.quit = QtGui.QPushButton("Quit", self)
self.setGeometry(300, 300, 250, 150)
self.statusBar().showMessage('Ready')
app = QtGui.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())
Now all I want to do , is add a Status. Now status is available in QtGui.QMainWindow
How can i use this fact to add it in the above program? In pyside coding , seems like for every component , we need to make a class and some connect to the main class..whata s the theory here?
I tried myself like this , but it did not work.
import sys
from PySide import QtCore, QtGui
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.quit = QtGui.QPushButton("Quit", self)
self.setGeometry(300, 300, 250, 150)
self.statusBar().showMessage('Ready')
self.s = MyStatus()
class MyStatus(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.statusBar().showMessage('Ready')
app = QtGui.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())
You do not need to make a class for every component, but if you want to modify/override each component's built-in function you need to make a class for it.
To add something to your main window, you simply have to create an object and add it to the layout. As follow:
import sys
from PySide import QtGui , QtCore
class MyStatusBar(QtGui.QStatusBar):
def __init__(self, parent=None):
super(MyStatusBar, self).__init__(parent)
#Override any functions, or define new function for our status bar here
class MyMainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
#Set the size of the window
self.setMinimumSize(300,300)
#Create a status bar, from our OWN class
self.status_bar = MyStatusBar(self)
self.status_bar.setGeometry(QtCore.QRect(0, 0, 50, 50))
self.status_bar.showMessage('Ready')
#Add a simple quit button, from the DEFAULT class
self.quit_button = QtGui.QPushButton(self)
self.quit_button.clicked.connect(self.close)
self.quit_button.setGeometry(QtCore.QRect(100, 100, 100, 35))
self.quit_button.setText("Close")
#Start the application
app = QtGui.QApplication(sys.argv)
top = MyMainWindow()
top.show()
app.exec_()
If you want a window with a status-bar, use QMainWindow: it has one built-in (and also a menu-bar, tool-bars, dock-widgets, etc). Other widgets don't have these built-in features, and so, quite naturally, they don't have things like a statusBar method. If you insist on doing things the hard way by not using QMainWindow, you will have to add all these features yourself.
Although I wouldn't recommend doing things this way, here is a simple demo that adds a status-bar to a QWidget:
import sys
from PySide import QtCore, QtGui
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.central_widget = QtGui.QWidget(self)
self.quit = QtGui.QPushButton("Quit", self)
self.setGeometry(300, 300, 250, 150)
layout = QtGui.QVBoxLayout(self.central_widget)
layout.addWidget(self.quit)
self.status = QtGui.QStatusBar(self)
layout = QtGui.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.widget)
layout.addStretch()
layout.addWidget(self.status)
self.status.showMessage('Ready')
app = QtGui.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())

Categories