why QTableWidget does not refresh after closing tab in pyqt5? - python

the below example is my problem which can not figure it out! i appreciate if anyone can help on this problem. i have set of data which in this example i am reading from csv file. in next step i am filling my QTableWidget with the data. The problem is when i change data and close the tab and open it, the data does not refresh.
here is my sample code:
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout
from PyQt5.QtWidgets import QAction, QTabWidget, QTableWidget, QTableWidgetItem
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(100, 100, 400, 400)
self.setup_ui()
self.show()
def setup_ui(self):
# toolbar
self.toolbar = self.addToolBar('Tool Bar')
self.test_btn = QAction('Test', self)
self.toolbar.addAction(self.test_btn)
self.test_btn.triggered.connect(self.load_data)
# tab
self.tabs = QTabWidget()
self.tab_test = QWidget()
self.tabs.setTabsClosable(True)
self.tabs.tabCloseRequested.connect(lambda tab_index: self.tabs.removeTab(tab_index))
self.setCentralWidget(self.tabs)
def load_data(self):
# get data
data = []
with open('test_data.csv', 'r') as f:
header = next(f).replace('\n', '').split(',')
for line in f:
data.append(line.replace('\n', '').split(','))
print(data)
# table and button widget
self.layout = QVBoxLayout()
self.tabs.addTab(self.tab_test, 'Test')
self.table = QTableWidget()
self.layout.addWidget(self.table)
self.tab_test.setLayout(self.layout)
# fill headers
self.table.setColumnCount(len(header))
for i, field_name in enumerate(header):
self.table.setHorizontalHeaderItem(i, QTableWidgetItem(field_name))
# fill data
for row, row_data in enumerate(data):
self.table.insertRow(row)
for col, data in enumerate(row_data):
self.table.setItem(row, col, QTableWidgetItem(str(data)))
def main():
app = QApplication([])
ui = MainWindow()
app.exec_()
if __name__ == '__main__':
main()
and this is the sample data for test_data.csv:
col_1,col_2
1,2
10,20
100,200
before updating data:
but after updating 100 to 1000 and 200 to 2000 in csv file then closing tab and opening it, the data has been updated but the items of table not!
what am i missing?

You're assigning a single QWidget instance at MainWindow.setup_ui, right here:
def setup_ui(self):
# toolbar
self.toolbar = self.addToolBar('Tool Bar')
self.test_btn = QAction('Test', self)
# .
# .
# tab
self.tabs = QTabWidget() # <--
# .
# .
# .
self.setCentralWidget(self.tabs)
This means that you're always adding the same QWidget over and over to the QTabWidget. So, when you call MainWindow.load_data, this function call self.tabs.addTab(tab_test, 'Test') does nothing, because the widget is already present on the table widget.
What you should do, is to create a new QWidget inside MainWindow.load_data, just like this:
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout
from PyQt5.QtWidgets import QAction, QTabWidget, QTableWidget, QTableWidgetItem
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(100, 100, 400, 400)
self.setup_ui()
self.show()
def setup_ui(self):
# toolbar
toolbar = self.addToolBar('Tool Bar')
test_btn = QAction('Test', self)
toolbar.addAction(test_btn)
test_btn.triggered.connect(self.load_data)
# tab
self.tabs = QTabWidget()
self.tabs.setTabsClosable(True)
self.tabs.tabCloseRequested.connect(lambda tab_index: self.tabs.removeTab(tab_index))
self.setCentralWidget(self.tabs)
def load_data(self):
# get data
data = []
with open('test_data.csv', 'r') as f:
header = next(f).replace('\n', '').split(',')
for line in f:
data.append(line.replace('\n', '').split(','))
print(data)
# table and button widget
layout = QVBoxLayout()
tab_test = QWidget() ## MOVED THIS LINE FROM setup_ui
self.tabs.addTab(tab_test, 'Test')
table = QTableWidget()
layout.addWidget(table)
tab_test.setLayout(layout)
# fill headers
table.setColumnCount(len(header))
for i, field_name in enumerate(header):
table.setHorizontalHeaderItem(i, QTableWidgetItem(field_name))
# fill data
for row, row_data in enumerate(data):
table.insertRow(row)
for col, data in enumerate(row_data):
table.setItem(row, col, QTableWidgetItem(str(data)))
def main():
app = QApplication([])
ui = MainWindow()
app.exec_()
if __name__ == '__main__':
main()
Note that I used local variables on setup_ui and load_data scopes. As most variables in there are not being used in other MainWindow methods, it's not needed to store them to self, unlike self.tabs. Also, as every tab should be independent of each other, it's not a good thing to overwrite the self.test_tab variable, unless you do need to store only the last added tab.

Related

How do I connect three or more windows in PyQt

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

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()

How to get the right table row index in pyQt5 contextMenuEvent

I need to get the table row index during a contextMenuEvent in pyQt5, but I keep getting odd row offsets. Since this is my first pyQt project, I'm quite lost. I have the following code.
def contextMenuEvent(self, event):
menu = QMenu(self)
openAction = menu.addAction('Open in browser')
action = menu.exec_(event.globalPos())
if action == openAction:
row = self.tableWidget.rowAt(event.y()) # Should this be event.globalY?
self.so_something(row)
Edit: adding code sample. You can see that when the execution gets to the event produced when right-clicking on a row, the printed number is not the correct row number. Usually it has an offset, and last rows produce -1.
import sys
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QLabel, QLineEdit, QWidget, \
QPushButton, QVBoxLayout, QHBoxLayout, QComboBox, \
QTableWidget, QHeaderView, QTableWidgetItem, \
QMenu, QApplication
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
# Window size and title
self.setWindowTitle('Test')
self.resize(700, 700)
self.setMinimumWidth(700)
# Label
addressLabel = QLabel('Testing')
addressLabel.setAlignment(Qt.AlignCenter)
# Dropdown
self.addressDropdown = QComboBox(self)
self.addressDropdown.addItem('A')
self.addressDropdown.addItem('B')
# Refresh button
refreshButton = QPushButton()
refreshButton.setMaximumSize(40, 40)
# Address layout
addressayout = QHBoxLayout()
addressayout.addWidget(addressLabel, 1)
addressayout.addWidget(self.addressDropdown, 7)
addressayout.addWidget(refreshButton)
# Balance label
balaceLabel = QLabel()
balaceLabel.setText('Testing')
balaceLabel.setAlignment(Qt.AlignCenter)
# Balance amount label
self.balaceAmountLabel = QLabel()
self.balaceAmountLabel.setText('Testing')
self.balaceAmountLabel.setAlignment(Qt.AlignCenter)
# Balance layout
balanceLayout = QVBoxLayout()
balanceLayout.addWidget(balaceLabel)
balanceLayout.addWidget(self.balaceAmountLabel)
# Transactions label
transactionsLabel = QLabel('Testing')
transactionsLabel.setAlignment(Qt.AlignCenter)
# Transactions table
self.tableWidget = QTableWidget()
self.tableWidget.setRowCount(10)
self.tableWidget.setColumnCount(1)
self.tableWidget.verticalHeader().setVisible(False)
self.tableWidget.horizontalHeader().setVisible(False)
self.tableWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.tableWidget.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
item = QTableWidgetItem('Testing')
item.setTextAlignment(Qt.AlignVCenter | Qt.AlignHCenter)
self.tableWidget.setItem(0, 0, item)
# Transactions layout
transactionsLayout = QVBoxLayout()
transactionsLayout.addWidget(transactionsLabel)
transactionsLayout.addWidget(self.tableWidget)
# Send label A
sendLabelA = QLabel('Send')
sendLabelA.setAlignment(Qt.AlignCenter)
# Send amount
self.sendAmount = QLineEdit()
self.sendAmount.setAlignment(Qt.AlignCenter)
# Send label B
sendLabelB = QLabel('to')
sendLabelB.setAlignment(Qt.AlignCenter)
# Send address
self.sendAddress = QLineEdit()
self.sendAddress.setAlignment(Qt.AlignCenter)
# Send button
sendButton = QPushButton()
sendButton.setMaximumSize(40, 40)
sendIcon = QIcon.fromTheme("mail-send")
sendButton.setIcon(sendIcon)
sendButton.setIconSize(QSize(24,24))
# Send layout
sendLayout = QHBoxLayout()
sendLayout.addWidget(sendLabelA)
sendLayout.addWidget(self.sendAmount, 2)
sendLayout.addWidget(sendLabelB)
sendLayout.addWidget(self.sendAddress, 4)
sendLayout.addWidget(sendButton)
# Window layout
layout = QVBoxLayout()
layout.addLayout(addressayout)
layout.addLayout(balanceLayout)
layout.addLayout(transactionsLayout)
layout.addLayout(sendLayout)
self.setLayout(layout)
def contextMenuEvent(self, event):
menu = QMenu(self)
openAction = menu.addAction('Open in browser')
action = menu.exec_(event.globalPos())
row = self.tableWidget.rowAt(event.y())
if action == openAction:
print(row)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
The coordinate that rowAt() method requires must be with respect to the viewport of the QTableWidget so you must convert the global position to local using mapFromGlobal() method:
def contextMenuEvent(self, event):
gp = event.globalPos()
menu = QMenu(self)
openAction = menu.addAction("Open in browser")
action = menu.exec_(gp)
vp_pos = self.tableWidget.viewport().mapFromGlobal(gp)
row = self.tableWidget.rowAt(vp_pos.y())
if action == openAction:
print(row)

widgets placement in tabs

So I have this code:
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QWidget, QAction, QTabWidget, QVBoxLayout, QHBoxLayout, QComboBox, QLabel
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
from lang import getLang
import sys, os, configparser
config = configparser.ConfigParser()
config.read("Settings.ini")
# Config get setting and change setting
#print(config.get("Main", "language"))
#config.set("Main", "language", "danish")
#with open("Settings.ini", "w") as cfg_file:
#config.write(cfg_file)
class App(QMainWindow):
def __init__(self):
super().__init__()
# Window Settings
self.x, self.y, self.w, self.h = 0, 0, 300, 200
self.setGeometry(self.x, self.y, self.w, self.h)
self.window = MainWindow(self)
self.setCentralWidget(self.window)
self.setWindowTitle("Window title") # Window Title
self.show()
class MainWindow(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
layout = QVBoxLayout(self)
# Run this after settings
self.lang = getLang(config.get("Main", "language"))
# Initialize tabs
tab_holder = QTabWidget() # Create tab holder
tab_1 = QWidget() # Tab one
tab_2 = QWidget() # Tab two
# Add tabs
tab_holder.addTab(tab_1, self.lang["tab_1_title"]) # Add "tab1" to the tabs holder "tabs"
tab_holder.addTab(tab_2, self.lang["tab_2_title"]) # Add "tab2" to the tabs holder "tabs"
# Create first tab
tab_1.layout = QVBoxLayout(self)
tab_2.layout = QVBoxLayout(self)
# Buttons
button_start = QPushButton(self.lang["btn_start"])
button_stop = QPushButton(self.lang["btn_stop"])
button_test = QPushButton(self.lang["btn_test"])
# Button Extra
button_start.setToolTip("This is a tooltip for the button!") # Message to show when mouse hover
button_start.clicked.connect(self.on_click)
button_stop.clicked.connect(self.on_click)
button_test.clicked.connect(self.on_click)
#button_start.setEnabled(False)
# comboBox
label_language = QLabel("Language")
combo_language = QComboBox(self)
combo_language.addItem(self.lang["language_danish"])
combo_language.addItem(self.lang["language_english"])
# Move widgets
combo_language.move(50, 150)
label_language.move(50, 50)
# Tab Binding
self.AddToTab(tab_1, button_start)
self.AddToTab(tab_1, button_stop)
self.AddToTab(tab_2, label_language)
self.AddToTab(tab_2, combo_language)
# Add tabs to widget
tab_1.setLayout(tab_1.layout)
tab_2.setLayout(tab_2.layout)
layout.addWidget(tab_holder)
self.setLayout(layout)
#pyqtSlot()
def on_click(self):
button = self.sender().text()
if button == self.lang["btn_start"]:
print("Dank")
elif button == self.lang["btn_stop"]:
print("Not dank")
def AddToTab(self, tab, obj):
tab.layout.addWidget(obj)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
Currently it creates a window that contains 2 buttons on tab one. A "Start" and "Stop" button with no actual function yet other than printing text.
On tab 2 I have a label saying "Language:" and a dropdown menu that contains "Danish" and "English".
The problem I have with this, is that it's placement is really really weird and annoying as shown here:
I'm not sure how to change the placement as I can't just use .move on the text label, buttons and dropdown menu since they are placed in tabs.
For example, on tab two I would like the label "Language:" to be right to the left of the dropdown menu.
The problem is caused because you are using QVBoxLayout to set the widgets inside each tab. The task of the layout is to manage the position and size of the widgets so you can not use move(), besides it is not necessary.
The solution is that you create a custom layout for each tab, but keeping an order will create a custom widget for each tab, in the case of the first tab I'll continue using QVBoxlayout but add a stretch so that the widget stays stuck in the top position. In the second case I will combine a QVBoxLayout with a QHBoxLayout, the QHBoxLayout will use it to place the QLabel and the QComboBox and the QVBoxLayout will use it to push the widgets to the top position.
Implementing the above, I have omitted the lang for obvious reasons, we get the following:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys, os, configparser
config = configparser.ConfigParser()
config.read("Settings.ini")
# Config get setting and change setting
#print(config.get("Main", "language"))
#config.set("Main", "language", "danish")
#with open("Settings.ini", "w") as cfg_file:
#config.write(cfg_file)
class App(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# Window Settings
self.x, self.y, self.w, self.h = 0, 0, 300, 200
self.setGeometry(self.x, self.y, self.w, self.h)
self.window = MainWindow(self)
self.setCentralWidget(self.window)
self.setWindowTitle("Window title") # Window Title
self.show()
class GeneralWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(GeneralWidget, self).__init__(parent)
lay = QtWidgets.QVBoxLayout(self)
# Buttons
button_start = QtWidgets.QPushButton("start") #self.lang["btn_start"])
button_stop = QtWidgets.QPushButton("stop") #self.lang["btn_stop"])
# Button Extra
button_start.setToolTip("This is a tooltip for the button!") # Message to show when mouse hover
button_start.clicked.connect(self.on_click)
button_stop.clicked.connect(self.on_click)
lay.addWidget(button_start)
lay.addWidget(button_stop)
lay.addStretch()
#QtCore.pyqtSlot()
def on_click(self):
button = self.sender().text()
if button == self.lang["btn_start"]:
print("Dank")
elif button == self.lang["btn_stop"]:
print("Not dank")
class OptionsWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(OptionsWidget, self).__init__(parent)
lay = QtWidgets.QVBoxLayout(self)
hlay = QtWidgets.QHBoxLayout()
lay.addLayout(hlay)
lay.addStretch()
label_language = QtWidgets.QLabel("Language")
combo_language = QtWidgets.QComboBox(self)
combo_language.addItem("item1") #self.lang["language_danish"])
combo_language.addItem("item2") #self.lang["language_english"])
hlay.addWidget(label_language)
hlay.addWidget(combo_language)
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent):
super(MainWindow, self).__init__(parent)
layout = QtWidgets.QVBoxLayout(self)
# Run this after settings
# self.lang = getLang(config.get("Main", "language"))
# Initialize tabs
tab_holder = QtWidgets.QTabWidget() # Create tab holder
tab_1 = GeneralWidget() # Tab one
tab_2 = OptionsWidget() # Tab two
# Add tabs
tab_holder.addTab(tab_1, "General") #self.lang["tab_1_title"]) # Add "tab1" to the tabs holder "tabs"
tab_holder.addTab(tab_2, "Options") #self.lang["tab_2_title"]) # Add "tab2" to the tabs holder "tabs"
layout.addWidget(tab_holder)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())

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