How do I connect three or more windows in PyQt - python

I have a complex program in which I need to connect multiple windows. Unfortunately, I seem to be not fully understanding the concept/steps necessary to do this so bonus points if anyone can explain the steps/process well. In my current program, I have a list of items. Once I select them by moving them over to the right list widget, I need them to go to the third window. The third window should be activated by clicking the dots on the second window. The program runs and shows the second window appropriately but the signal/slot connection of the dots button does not work. However, the rest of the code is working because if I switch the toolkit to show the third window, that part is performing as expected. My code is below, and again, no errors are being returned, but clicking the dots button on the second window does nothing.
Also, a question - do I instantiate the third window within the second class, or only within the main window? Again, struggling to fully understand the process and I will need to do this multiple more times.
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QListWidget, QLineEdit, QTextEdit, QGridLayout, QHBoxLayout, QVBoxLayout, QSizePolicy, QFileDialog, QTabWidget, QCheckBox
import PyQt5.QtGui as qtg
import glob
import os
from PyQt5.QtCore import Qt, QSettings
import inspect
from PyQt5 import QtCore
import pandas as pd
import pathlib
import pyreadstat
import json
class ThirdWindow(QWidget):
def __init__(self):
super().__init__()
self.layout = QGridLayout()
self.setLayout(self.layout)
self.allVariables = QListWidget()
self.variablesSelected = QListWidget()
#self.allVariables.insertItem(0, 'Hello')
self.layout.addWidget(self.allVariables, 1,0)
self.layout.addWidget(self.variablesSelected, 1, 1)
def setItems(self, items):
self.allVariables.clear()
for item in items:
self.allVariables.addItem(item)
class SecondWindow(QWidget):
def __init__(self):
super().__init__()
##not sure if I am supposed to instantiate this here or only in the main window class
self.thirdWindow = ThirdWindow()
self.layout = QGridLayout(self)
self.by = QLabel("By")
self.byVariables = QLineEdit()
self.byButton = QPushButton("...")
self.layout.addWidget(self.by, 1, 0)
self.layout.addWidget(self.byVariables, 2, 0)
self.layout.addWidget(self.byButton, 2, 1)
def seconddWindowConnections(self):
self.byButton.clicked.connect(self.show_third_window)
#self.buttons['Toolkit'].clicked.connect(self.show_new_window)
def show_third_window(self):
self.thirdWindow.show()
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# Add a title
self.setWindowTitle("GUI Querying Program")
self.layout = QHBoxLayout()
self.setLayout(self.layout)
self.initUI()
self.setButtonConnections()
self.sw = SecondWindow()
self.tw = ThirdWindow()
def initUI(self):
subLayouts = {}
subLayouts['LeftColumn'] = QGridLayout()
self.layout.addLayout(subLayouts['LeftColumn'],1)
# Buttons
self.buttons = {}
self.buttons['addVariable'] = QPushButton('>')
self.buttons['removeVariable'] = QPushButton('<')
self.buttons['Toolkit'] = QPushButton('Toolkit')
self.variables = QListWidget()
self.selectedVariables = QListWidget()
subLayouts['LeftColumn'].addWidget(self.variables, 7,0,4,1)
subLayouts['LeftColumn'].addWidget(self.selectedVariables, 7,1,4,1)
subLayouts['LeftColumn'].addWidget(self.buttons['addVariable'], 10,0,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['removeVariable'], 10,1,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['Toolkit'], 11,1,1,1)
names = ['apple', 'banana', 'Cherry']
self.variables.insertItems(0, names)
def setButtonConnections(self):
self.buttons['addVariable'].clicked.connect(self.add_variable)
self.buttons['Toolkit'].clicked.connect(self.show_new_window)
self.buttons['Toolkit'].clicked.connect(self.add_selected_variables)
def add_variable(self):
for item in self.variables.selectedItems():
self.selectedVariables.addItem(item.clone())
def show_new_window(self):
self.sw.show()
def add_selected_variables(self):
items = []
for i in range(self.selectedVariables.count()):
items.append(self.selectedVariables.item(i).clone())
self.tw.setItems(items)
if __name__ == "__main__":
import sys
app = QApplication([])
mw = MainWindow()
mw.show()
app.exec()

The main issue with your code is that secondWindowConnections is never called so the button actually does nothing. I corrected that and fixed a few other issues I found in my example below. I left out the bits where I made no changes and all the changes I did make I made inline notes explaining them:
class SecondWindow(QWidget):
def __init__(self):
super().__init__()
self.thirdWindow = None # dont initialize until neccessary
self.thirdWindowItems = []
self.layout = QGridLayout(self)
self.by = QLabel("By")
self.byVariables = QLineEdit()
self.byButton = QPushButton("...")
self.layout.addWidget(self.by, 1, 0)
self.layout.addWidget(self.byVariables, 2, 0)
self.layout.addWidget(self.byButton, 2, 1)
self.secondWindowConnections() # Run this to setup the
# signal for the third window.
def secondWindowConnections(self): # this had a typo
self.byButton.clicked.connect(self.show_third_window)
def show_third_window(self):
if self.thirdWindow is None: # if window has been created yet
self.thirdWindow = ThirdWindow() # create window
if not self.thirdWindow.isVisible(): # if window is showing
self.thirdWindow.show() # show window
self.thirdWindow.setItems(self.thirdWindowItems) # send items to window
def send_items(self, items): # this is to collect the variable that
self.thirdWindowItems = items # move to the third window
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# Add a title
self.setWindowTitle("GUI Querying Program")
self.layout = QHBoxLayout()
self.setLayout(self.layout)
self.initUI()
self.setButtonConnections()
self.sw = None # dont initialize until neccessary.
def initUI(self):
subLayouts = {}
subLayouts['LeftColumn'] = QGridLayout()
self.layout.addLayout(subLayouts['LeftColumn'],1)
self.buttons = {}
self.buttons['addVariable'] = QPushButton('>')
self.buttons['removeVariable'] = QPushButton('<')
self.buttons['Toolkit'] = QPushButton('Toolkit')
self.variables = QListWidget()
self.selectedVariables = QListWidget()
subLayouts['LeftColumn'].addWidget(self.variables, 7,0,4,1)
subLayouts['LeftColumn'].addWidget(self.selectedVariables, 7,1,4,1)
subLayouts['LeftColumn'].addWidget(self.buttons['addVariable'], 10,0,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['removeVariable'], 10,1,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['Toolkit'], 11,1,1,1)
names = ['apple', 'banana', 'Cherry']
self.variables.insertItems(0, names)
def setButtonConnections(self):
self.buttons['addVariable'].clicked.connect(self.add_variable)
self.buttons['Toolkit'].clicked.connect(self.show_new_window)
# self.buttons['Toolkit'].clicked.connect(self.add_selected_variables)
# only use one connnect slot
def add_variable(self):
for item in self.variables.selectedItems():
self.selectedVariables.addItem(item.clone())
def show_new_window(self):
if self.sw is None: # check if window has been constructed
self.sw = SecondWindow() # construct window
if not self.sw.isVisible(): # If winow is not showing
self.sw.show() # show window
self.sw.send_items(self.add_selected_variables()) # send selected
# variables to second window
def add_selected_variables(self):
items = []
for i in range(self.selectedVariables.count()):
items.append(self.selectedVariables.item(i).clone())
# self.tw.setItems(items) ... self.tw doesnt exist so return them
return items

Related

Data not being added to table with PyQt5

My code is supposed to add data to a two column table when "add" button is clicked. The problem is that when the "add" button is clicked, only the empty row is being added. Can someone please let me know what is wrong? Below is the part of the code that adds data1 and data2 to a table on the right side of the layout. The function add_entry is where the data is being added.
# Import dependencies
from PyQt5.QtWidgets import (QWidget, QApplication, QTableWidget, QTableWidgetItem,QHBoxLayout, QVBoxLayout, QHeaderView, QPushButton, QDialog,
QLabel, QFileDialog, QMainWindow, QAction, QLineEdit)
from PyQt5.Qt import Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtChart import QChart, QChartView, QLineSeries
import sys
import pandas as pd
import math
# ------------------------------------------------------UI-main----------------------------------------------------------------------------------
# Creates a QApplication instance
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.items=0
# Creates table on the left size
self.table_l = QTableWidget()
self.table_l.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# Creates layout object for the right side
self.layoutRight = QVBoxLayout()
# Creates chart widget
self.chartView = QChartView()
# Smooths the edge of the chart
self.chartView.setRenderHint(QPainter.Antialiasing)
# Creates table on the right size
self.table_r = QTableWidget()
self.table_r.setColumnCount(2)
# self.table_r.setRowCount()
self.table_r.setHorizontalHeaderLabels(('Data1', 'Data2'))
self.table_r.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.table_r.setMaximumSize(600, 300)
self.lineEditData1 = QLineEdit()
self.lineEditData2 = QLineEdit()
# Create push buttons
self.buttonAdd = QPushButton('Add')
self.buttonClear = QPushButton('Clear')
self.buttonQuit = QPushButton('Quit')
self.buttonAdd.setEnabled(False)
self.layoutRight.setSpacing(10)
self.layoutRight.addWidget(self.table_r, 50)
self.layoutRight.addWidget(QLabel('data1'))
self.layoutRight.addWidget(self.lineEditData1)
self.layoutRight.addWidget(QLabel('data2'))
self.layoutRight.addWidget(self.lineEditData2)
self.layoutRight.addWidget(self.buttonAdd)
self.layout = QHBoxLayout()
self.layout.addWidget(self.table_l, 50)
self.setLayout(self.layout)
self.layout.addLayout(self.layoutRight, 50)
# Connect button to function functions
self.buttonQuit.clicked.connect(lambda:app.quit())
self.buttonAdd.clicked.connect(self.add_entry)
self.buttonClear.clicked.connect(self.reset_table)
self.lineEditData1.textChanged[str].connect(self.check_disable)
self.lineEditData2.textChanged[str].connect(self.check_disable)
def add_entry(self):
Data1 = self.lineEditData1.text()
Data2 = self.lineEditData2.text()
try:
Data1Item = QTableWidgetItem(int(Data1))
Data2Item = QTableWidgetItem(float(Data2))
Data2Item.setTextAlignment(Qt.AlignRight | Qt.AlignCenter)
self.table_r.insertRow(self.items)
self.table_r.setItem(self.items, 0, Data1Item)
self.table_r.setItem(self.items, 1, Data2Item)
self.items +=1
# after passing the item, clear the field by entering an empty string
self.lineEditData1.setText('')
self.lineEditData2.setText('')
except ValueError:
pass
# Creates main window object instance
class MainWindow(QMainWindow):
def __init__(self, widget):
super().__init__()
self.setWindowTitle('test')
self.resize(1200, 1200)
self.menuBar = self.menuBar()
self.fileMenu = self.menuBar.addMenu('File')
# import wind speed data
importAction = QAction('Open File', self)
importAction.setShortcut('Ctrl+O')
# exit action
exitAction = QAction('Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(lambda: app.quit())
self.fileMenu.addAction(importAction)
self.fileMenu.addAction(exitAction)
self.setCentralWidget(widget)
if __name__ =='__main__':
# don't auto scale when drag app to a different monitor
#QGuiApplication.setHightDpiScaleFactorRoundingPolicy(Qt.HightDpiScaleFactorRoundingPolicy.PassThrough)
app = QApplication(sys.argv)
w = MyApp()
demo = MainWindow(w)
demo.show()
try:
sys.exit(app.exec())
except SystemExit:
print('Closing window...')
The objective of exceptions is not to hide errors but to know how to prevent them, so they must be as small as possible so as not to hide other errors. In this case, QTableWidgetItem accepts a string as an argument and not numerical values, therefore an exception is thrown preventing the code that adds the items from being executed. The solution is to use the setData() method of the QTableWidgetItem:
def add_entry(self):
data1 = self.lineEditData1.text()
data2 = self.lineEditData2.text()
try:
value1 = int(data1)
value2 = float(data2)
except ValueError:
print("failed conversion")
return
else:
data1_item = QTableWidgetItem()
data1_item.setData(Qt.DisplayRole, value1)
data2_item = QTableWidgetItem()
data2_item.setData(Qt.DisplayRole, value2)
data2_item.setTextAlignment(Qt.AlignRight | Qt.AlignCenter)
row = self.table_r.rowCount()
self.table_r.insertRow(row)
self.table_r.setItem(row, 0, data1_item)
self.table_r.setItem(row, 1, data2_item)
self.lineEditData1.clear()
self.lineEditData2.clear()

QGroupBox buttons not showing

So basically I'm trying to make an updating QGroupBox with some QPushButtons inside of it, here's the "updating" method and it is always called right after the list is changed:
def repaintList(self):
btn_ls = []
for i in range(len(self.list)):
btn_ls.append(buttons.QPushButton("t"))
layout = QVBoxLayout()
for i in range(len(btn_ls)):
layout.addWidget(btn_ls[i])
it's pretty simple, I have a method that updates the list for me and I've tested the functionality with print(len(self.list)) and print(btn_ls) enough to know that the list updating works, and that the btn_ls is made properly, but I'm not sure why it's not updating on the actual screen.
I've made an example of a simplified version of what I'm trying to accomplish:
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class TestWindow(QWidget):
def __init__(self):
super(TestWindow,self).__init__()
self.list = []
self.cont = TestContainer(self.list, "Test Box", self)
self.adder = AdderButton("Add one", self, self.list, self.cont)
layout = QVBoxLayout()
layout.addWidget(self.cont)
self.setLayout(layout)
self.setGeometry(200,200,200,200)
class TestContainer(QGroupBox):
def __init__(self,ls,ttl,pare):
super(TestContainer,self).__init__(ttl,pare)
self.list = ls
self.repaintButtons()
def repaintButtons(self):
btn_ls = []
for i in range(len(self.list)):
btn_ls.append(QPushButton(str(i),self))
print(self.list)
layout = QVBoxLayout()
for i in range(len(btn_ls)):
layout.addWidget(btn_ls[i])
self.setLayout(layout)
class AdderButton(QPushButton):
def __init__(self,txt,pare,ls,displ):
super(AdderButton,self).__init__(txt,pare)
self.disp = displ
self.list = ls
self.clicked.connect(self.addOne)
def addOne(self):
self.list.append(1)
self.disp.repaintButtons()
def main():
app = QApplication(sys.argv)
tw = TestWindow()
tw.show()
app.exec()
if __name__ == "__main__":
main()
The desired result is that every time I press the button a new QPushButton would appear on the screen...
After doing more research, I came across the update() function which basically repaints the QGroupBox. It works in the sense that it adds one button at a time and updates each time a button is added.
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class TestWindow(QWidget):
def __init__(self):
super(TestWindow,self).__init__()
self.list = []
self.cont = TestContainer(self.list, "Test Box", self)
self.adder = AdderButton("Add one", self, self.list, self.cont)
layout = QVBoxLayout()
layout.addWidget(self.cont)
self.setLayout(layout)
self.setGeometry(200,200,200,200)
class TestContainer(QGroupBox):
def __init__(self,ls,ttl,pare):
super(TestContainer,self).__init__(ttl,pare)
self.list = ls
self.layout = QVBoxLayout()
self.setLayout(self.layout)
def addButton(self):
self.layout.addWidget(QPushButton("thing"))
class AdderButton(QPushButton):
def __init__(self,txt,pare,ls,displ):
super(AdderButton,self).__init__(txt,pare)
self.disp = displ
self.list = ls
self.clicked.connect(self.addOne)
def addOne(self):
self.list.append(1)
self.disp.addButton()
self.disp.update()
def main():
app = QApplication(sys.argv)
tw = TestWindow()
tw.show()
app.exec()
if __name__ == "__main__":
main()
I has to use self.layout() instead of layout to see widgets.
def repaintButtons(self):
btn_ls = []
for i in range(len(self.list)):
btn_ls.append(QPushButton(str(i),self))
print(self.list)
layout = QVBoxLayout()
self.setLayout(layout) # it has to be before `self.layout()`
for i in range(len(btn_ls)):
self.layout().addWidget(btn_ls[i])
BTW: self.setLayout() has to be before self.layout()
But there is other problem.
Using again
layout = QVBoxLayout()
self.setLayout(layout)
doesn't remove previous layout and it doesn't remove previous buttons and finally it adds again the same buttons to layout.
You would need add only new buttons to layout instead of adding all buttons again.
Or you would have to remove widgets from layout before adding them again.
EDIT:
Code which adds new button without removing previous buttons
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class TestWindow(QWidget):
def __init__(self):
super(TestWindow,self).__init__()
self.list = []
self.container = TestContainer("Test Box", self, self.list)
self.adder = AdderButton("Add one", self, self.container)
layout = QVBoxLayout()
layout.addWidget(self.container)
self.setLayout(layout)
self.setGeometry(200,200,200,200)
class TestContainer(QGroupBox):
def __init__(self, title, parent, lst):
super(TestContainer,self).__init__(title, parent)
self.list = lst
layout = QVBoxLayout()
self.setLayout(layout)
# create buttons at start using `self.list`
for item in self.list:
self.layout().addWidget(QPushButton(str(item), self))
def addButton(self, item):
# add new item to `self.list` and create new button
self.list.append(item)
self.layout().addWidget(QPushButton(str(item), self))
class AdderButton(QPushButton):
def __init__(self, text, parent, target):
super(AdderButton,self).__init__(text, parent)
self.target = target
self.clicked.connect(self.addOne)
def addOne(self):
self.target.addButton(1)
def main():
app = QApplication(sys.argv)
tw = TestWindow()
tw.show()
app.exec()
if __name__ == "__main__":
main()
There are examples how to remove items but they don't work for me
Clear all widgets in a layout in pyqt
PyQt How to remove a layout from a layout

How to populate several QComboBox from a QFileSystemModel?

How does one use a QFileSystemModel to populate several QComboBox with subdirectories?
I have built a project management tool that allows me to create and manage my projects. I am currently using a combination of os.listdir and json to populate and validate my QComboboxes. But I am trying to learn a more modelview approach with QFileSystemModel.
So this is what I have:
class FileSystemModel(QW.QFileSystemModel):
def __init__(self, root, parent=None):
QW.QFileSystemModel.__init__(self, parent)
self.root = root
self.rootIndex = self.setRootPath(root)
class Window(QW.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__()
self.init()
def init(self):
layout = QW.QVBoxLayout()
self.cbox = QW.QComboBox()
self.cbox2 = QW.QComboBox()
self.model = FileSystemModel("C:\\projects\\")
self.cbox.setModel(self.model)
self.cbox2.setModel(self.model)
self.cbox.setRootModelIndex(self.model.rootIndex)
self.cbox.currentIndexChanged.connect(self._indexChanged)
layout.addWidget(self.cbox)
layout.addWidget(self.cbox2)
self.setLayout(layout)
def _indexChanged(self):
row = self.sender().currentIndex()
index = self.sender().rootModelIndex().child(row, 0)
self.cbox2.setRootModelIndex(index)
def main():
app = QW.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
I was attempting to repopulate the cbox2 using the index from cbox, but with my code it doesn't seem to work - it just stays empty.
Okay here is modified version of what you had:
from sys import exit as sysExit
from PyQt5.QtCore import QDir, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QFileSystemModel, QHBoxLayout, QComboBox
class SysDirModel(QFileSystemModel):
def __init__(self, DirPath):
QFileSystemModel.__init__(self)
self.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs)
self.setReadOnly(True)
# Property
self.setRootPath(DirPath)
# Property
self.RootIndex = self.index(DirPath)
class SysFileModel(QFileSystemModel):
def __init__(self, DirPath):
QFileSystemModel.__init__(self)
self.setFilter(QDir.NoDotAndDotDot | QDir.Files)
self.setReadOnly(True)
# Property
self.setRootPath(DirPath)
# Property
self.RootIndex = self.index(DirPath)
def ResetPath(self, DirPath):
self.setRootPath(DirPath)
self.RootIndex = self.index(DirPath)
class MainWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
self.setGeometry(150, 150, 450, 100)
# If you use forward slash this works in Windows as well and it is cleaner
self.SysDirs = SysDirModel('C:/projects/')
self.SysFils = SysFileModel('C:/projects/')
# Setup first ComboBox
self.cbxDirs = QComboBox()
self.cbxDirs.setMinimumWidth(200)
self.cbxDirs.setModel(self.SysDirs)
self.cbxDirs.setRootModelIndex(self.SysDirs.RootIndex)
# This sends a Signal to a predefined Slot
self.cbxDirs.currentIndexChanged.connect(self.IndexChanged)
self.cbxFiles = QComboBox()
self.cbxFiles.setMinimumWidth(200)
self.cbxFiles.setModel(self.SysFils)
self.cbxFiles.setRootModelIndex(self.SysFils.RootIndex)
HBox = QHBoxLayout()
HBox.addWidget(self.cbxDirs)
HBox.addStretch(1)
HBox.addWidget(self.cbxFiles)
self.setLayout(HBox)
# This is the receiver of a Signal (aka Slot) so it ought to be used as such
#pyqtSlot(int)
def IndexChanged(self, RowIdx):
# Get your Current DirPath based on the Selected Value
index = self.cbxDirs.rootModelIndex().child(RowIdx, 0)
DirPath = self.cbxDirs.model().filePath(index)
# Reset what ComboBox 2's Model and what it is looking at
self.cbxFiles.clear()
self.SysFils.ResetPath(DirPath)
self.cbxFiles.setModel(self.SysFils)
if __name__ == '__main__':
MainThred = QApplication([])
MainGui = MainWindow()
MainGui.show()
sysExit(MainThred.exec_())

PyQt: Adding user input into list

I am trying to make a list box where a user can enter more items to the list. I have a list box set up with an add button. The add button opens up a user input box where it will direct the user to enter a value. However, I am have issue with passing on the value of user input to the add onto the list. Any suggestions would help. Below is my code:
List box
from input_box import *
class list_form(QtGui.QWidget):
def __init__(self,list_of_items,open_text,parent= None):
super(list_form, self).__init__()
global output_path
output_path = output_path_i
grid = QtGui.QGridLayout()
grid.setSpacing(10)
self.widget = QtGui.QWidget()
self.layout = QtGui.QGridLayout(self.widget)
open_message = QtGui.QLabel(open_text)
grid.addWidget(open_message,0,0,2,4)
self.lst = QtGui.QListWidget()
grid.addWidget(self.lst,3, 0,1,4)
for i in list_of_items:
self.lst.addItem(str(i))
self.setLayout(grid)
add = QtGui.QPushButton('Add')
grid.addWidget(add,50,0)
add.clicked.connect(self.add_button)
def add_button(self):
self.input_box = input_box()
self.input_box.setWindowTitle("Window 2")
self.input_box.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = list_form(list(xrange(100)),"List of Values")
window.setWindowTitle('Window 1')
window.show()
sip.setdestroyonexit(False)
sys.exit(app.exec_())
Input box
import sys
from PyQt4 import QtCore, QtGui
import sip
class input_box(QtGui.QWidget):
def __init__(self,parent= None):
super(input_box, self).__init__()
grid = QtGui.QGridLayout()
grid.setSpacing(10)
self.widget = QtGui.QWidget()
self.layout = QtGui.QGridLayout(self.widget)
open_message = QtGui.QLabel("Enter Value:")
grid.addWidget(open_message,0,0,2,3)
self.txt = QtGui.QLineEdit()
grid.addWidget(self.txt,2, 0,1,2)
self.setLayout(grid)
save = QtGui.QPushButton('Save')
grid.addWidget(save,50,0)
a = save.clicked.connect(self.save)
save.clicked.connect(self.close)
cancel = QtGui.QPushButton('Cancel')
grid.addWidget(cancel,50,1)
cancel.clicked.connect(self.close)
def save(self):
value = self.txt.text()
return value
If you want to get data from a window you should use a QDialog instead of a QWidget, connect the clicked signal of save and cancel to the accept and reject slot, respectively:
input_box.py
from PyQt4 import QtCore, QtGui
class Input_Box(QtGui.QDialog):
def __init__(self,parent= None):
super(Input_Box, self).__init__(parent)
open_message = QtGui.QLabel("Enter Value:")
self.txt = QtGui.QLineEdit()
save = QtGui.QPushButton('Save', clicked=self.accept)
cancel = QtGui.QPushButton('Cancel', clicked=self.reject)
grid = QtGui.QGridLayout(self)
grid.setSpacing(10)
grid.addWidget(open_message, 0, 0)
grid.addWidget(self.txt, 1, 0, 1, 2)
grid.addWidget(save, 2, 0)
grid.addWidget(cancel, 2, 1)
self.setFixedSize(self.sizeHint())
def save(self):
value = self.txt.text()
return value
Then you use the exec_() method that returns a code if it is called accept or reject, and according to that the data must be obtained and added. On the other hand, do not use global variables because they are a headache when debugging.
from PyQt4 import QtCore, QtGui
from input_box import Input_Box
class list_form(QtGui.QWidget):
def __init__(self,list_of_items,open_text,parent= None):
super(list_form, self).__init__()
open_message = QtGui.QLabel(open_text)
self.lst = QtGui.QListWidget()
self.lst.addItems([str(i) for i in list_of_items])
add = QtGui.QPushButton('Add', clicked=self.add_button)
grid = QtGui.QGridLayout(self)
grid.setSpacing(10)
grid.addWidget(open_message)
grid.addWidget(self.lst)
grid.addWidget(add)
#QtCore.pyqtSlot()
def add_button(self):
input_box = Input_Box()
input_box.setWindowTitle("Window 2")
if input_box.exec_() == QtGui.QDialog.Accepted:
val = input_box.save()
it = QtGui.QListWidgetItem(val)
self.lst.addItem(it)
self.lst.scrollToItem(it)
if __name__ == "__main__":
import sys
import sip
app = QtGui.QApplication(sys.argv)
window = list_form(list(range(100)),"List of Values")
window.setWindowTitle('Window 1')
window.show()
sip.setdestroyonexit(False)
sys.exit(app.exec_())
The advantage of using QDialog is the decoupling between the classes, for example there is no need to pass a QListWidget as the other answer suggests making it possible that you can use the same dialog for other purposes.
You need to arrange for the action taken when the save button is pressed to pass information back to the list widget. There's more than one way to do it, but just returning the data won't get it done.
Here's an example of the sort of thing that will work - though other approaches are possible.
Change the input_box constructor so it keeps a reference to the list widget which it expects:
class input_box(QtGui.QWidget):
def __init__(self, list_widget, parent= None):
super(input_box, self).__init__()
self.list_widget = list_widget
...
Change the call to the constructor to provide that information:
def add_button(self):
self.input_box = input_box(self.lst)
self.input_box.setWindowTitle("Window 2")
self.input_box.show()
And then use that information in the save method to add to the list widget:
def save(self):
value = self.txt.text()
self.list_widget.addItem(value)
Bingo!
An alternative approach could be to arrange for the input_box to emit a signal with the new value in it, and connect that to a slot on the list_form, or on the list_widget. Or in the input_box you could navigate via its parent to the list_widget. But I think mine is simple and straightforward.

QT QButtonGroup.checkedId() returns only one checked button

I created a QButtonGroup (there might be unused artefacts in the code. Please ignore them). I would like to retrieve the Id of all checked radio buttons. However, the QButtonGroup.checkedId() does return only one button at a time.
Is it possible to retrieve a list of all checked RadioButtons ?
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QWidget, QAction, QTabWidget, QVBoxLayout,\
QHBoxLayout, QGridLayout, QSplitter, QFrame, QTextEdit, QFileDialog, QGroupBox,\
QButtonGroup, QLabel, QRadioButton
class MyRadioButtonGroup(QWidget):
"""
creates radio buttons from a list
"""
def __init__(self, label, instructions, name_list):
super().__init__()
self.title_label = QLabel(label)
self.name_list = name_list
# create the widget
self.layout = QVBoxLayout()
self.group_box = QGroupBox(instructions)
self.button_group = QButtonGroup()
self.button_group.setExclusive(False)
# create the buttons
self.button_list = [QRadioButton(item) for item in self.name_list]
self.button_layout = QVBoxLayout()
for i, button in enumerate(self.button_list):
self.button_layout.addWidget(button)
self.button_group.addButton(button)
self.button_group.setId(button, i)
self.group_box.setLayout(self.button_layout)
# create the main layout
self.main_layout = QVBoxLayout()
self.main_layout.addWidget(self.title_label)
self.main_layout.addWidget(self.group_box)
self.setLayout(self.main_layout)
# method for getting the state of the buttons
def get_states(self):
states = self.button_group.checkedId()
return states
class MainApp(QMainWindow):
""" This creates the main Window and configures its look
"""
def __init__(self):
'''
Initializes the class with standard parameters
'''
super().__init__()
self.title = 'Data Analysis App'
# start at top left corner of the screen
self.left = 0
self.top = 0
# set the window height and width
self.width = 1280
self.height = 780
# set the styles of fonts etc.
self.setStyleSheet('font-size: 16pt')
# call the makeUI method
self.makeUI()
def makeUI(self):
# set window geometry
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
# generate the tab layout
self.table_widget = MyTableWidget()
self.setCentralWidget(self.table_widget)
# apparently we have to call the show method in order to show it
self.show()
class MyTableWidget(QWidget):
"""
Initializes the tab layout
"""
def __init__(self):
super().__init__()
self.layout = QVBoxLayout(self)
# initialize the tab screens
self.tabs = QTabWidget()
# add the tab screens to the tab widget
self.tabs.resize(600, 600)
# make the layout of the tabs
self.make_tab1()
# initialize the tabs
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
def make_tab1(self):
"""Style of tab 1 is created here"""
'''Defines master layout of tab1'''
tab1 = QWidget()
tab1.layout = QHBoxLayout()
'''Button Section'''
# converts the data into excel and pandas dataframe
btn_test = QPushButton('Test')
btn_test.clicked.connect(self.test)
name_list = ['A', 'B', 'C']
self.radio_buttons = MyRadioButtonGroup('TEST', 'Check more than one', name_list)
'''Page layout section '''
tab1.layout.addWidget(btn_test)
tab1.layout.addWidget(self.radio_buttons)
tab1.setLayout(tab1.layout)
self.tabs.addTab(tab1, 'Test Radio Buttons')
''' Methods section
'''
def test(self):
print(self.radio_buttons.get_states())
if __name__ == '__main__':
""" This code block is always the same and makes sure that e.g. STRG+C kills the window etc.
"""
app = QApplication(sys.argv)
ex = MainApp()
sys.exit(app.exec())
You could just retrieve them with a list comprehension:
def test(self):
print([i for i, button in enumerate(self.radio_buttons.button_group.buttons()) if button.isChecked()])

Categories

Resources