Using Widget To Display Multiple Graphs Within Main Window - python

I have 2 widgets inside of MainWindow, one of which is the main chart that will cover the top 1/3 of the page (StockChart) but the other widget needs to be made up of 4 graphs equally split up on the bottom 2/3 of the page (ExtraCharts), the problem is I'm having a hard time getting them to show up and even run without errors, here is the current code:
import sys
from PySide6.QtCore import *
from PySide6.QtWidgets import *
from PySide6.QtGui import *
from PySide6.QtCharts import *
from sqlite import MySql
class MainWindow(QMainWindow):
def __init__(self, db, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowState(Qt.WindowMaximized)
self.setWindowTitle("Indicator Tool")
self.db = db
self.data = db.view_chart_data("5m")
self.stockWidget = StockChart(self)
self.extraCharts = ExtraCharts(self)
self.initUI()
def initUI(self):
grid = QGridLayout()
grid.setRowStretch(0, 1)
grid.setRowStretch(1, 2)
#Widget, row, column, rowspan, colspan
grid.addWidget(self.stockWidget.chartView, 0, 0, 2, 2)
grid.addWidget(self.extraCharts, 1, 0, 2, 2)
centralWidget = QWidget()
centralWidget.setLayout(grid)
self.setCentralWidget(centralWidget)
class StockChart(QWidget):
def __init__(self, parent):
super(StockChart, self).__init__(parent)
self.data = parent.data
self.drawChart()
def drawChart(self):
series = QCandlestickSeries()
for row in self.data:
time = row[1] * 1000
open = row[2]
high = row[3]
low = row[4]
close = row[5]
volume = row[6]
series.append(QCandlestickSet(open, high, low, close, time))
self.chart = QChart()
self.chart.legend().hide()
self.chart.addSeries(series)
self.chart.createDefaultAxes()
self.chart.setTitle('/ES Candlestick Chart')
self.chartView = QChartView(self.chart)
self.chartView.setRenderHint(QPainter.Antialiasing)
class ExtraCharts(QWidget):
def __init__(self, parent):
super(ExtraCharts, self).__init__(parent)
self.data = parent.data
self.drawChart()
def drawChart(self):
series = QCandlestickSeries()
for row in self.data:
time = row[1] * 1000
open = row[2]
high = row[3]
low = row[4]
close = row[5]
volume = row[6]
series.append(QCandlestickSet(open, high, low, close, time))
chart_1 = QChart()
chart_1.legend().hide()
chart_1.addSeries(series)
chart_1.createDefaultAxes()
chart_1.setTitle('Chart 1')
chart_2 = QChart()
chart_2.legend().hide()
chart_2.addSeries(series)
chart_2.createDefaultAxes()
chart_2.setTitle('Chart 2')
chart_3 = QChart()
chart_3.legend().hide()
chart_3.addSeries(series)
chart_3.createDefaultAxes()
chart_3.setTitle('Chart 3')
chart_4 = QChart()
chart_4.legend().hide()
chart_4.addSeries(series)
chart_4.createDefaultAxes()
chart_4.setTitle('Chart 4')
chartView_1 = QChartView(chart_1)
chartView_1.setRenderHint(QPainter.Antialiasing)
chartView_2 = QChartView(chart_2)
chartView_2.setRenderHint(QPainter.Antialiasing)
chartView_3 = QChartView(chart_3)
chartView_3.setRenderHint(QPainter.Antialiasing)
chartView_4 = QChartView(chart_4)
chartView_4.setRenderHint(QPainter.Antialiasing)
grid = QGridLayout()
# grid.setRowStretch(0, 1)
# grid.setRowStretch(1, 2)
#Widget, row, column, rowspa, colspan
grid.addWidget(chartView_1, 0, 0)
grid.addWidget(chartView_2, 0, 1)
grid.addWidget(chartView_3, 1, 0)
grid.addWidget(chartView_4, 1, 1)
chart_1.show()
chart_2.show()
chart_3.show()
chart_4.show()
if __name__ == "__main__":
app = QApplication([])
db = MySql()
window = MainWindow(db)
window.show()
sys.exit(app.exec())
and as of right now im getting this error:
"Can not find axis on the chart."
Segmentation fault (core dumped)
I know my method of doing this is probably wrong.

The problem is that a QXSerie can only be part of a QChart, and in your case you are creating violating that rule.
On the other hand, it is not good to access parent properties such as data, instead it is better to make the method receive the data.
import sys
from functools import cached_property
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QApplication,
QGridLayout,
QMainWindow,
QVBoxLayout,
QWidget,
)
from PySide6.QtGui import QPainter
from PySide6.QtCharts import QCandlestickSeries, QCandlestickSet, QChart, QChartView
from sqlite import MySql
class MainWindow(QMainWindow):
def __init__(self, db, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowState(Qt.WindowMaximized)
self.setWindowTitle("Indicator Tool")
self.db = db
data = db.view_chart_data("5m")
self.stockWidget = StockChart()
self.extraCharts = ExtraCharts()
self.stockWidget.drawChart(data)
self.extraCharts.drawChart(data)
self.initUI()
def initUI(self):
grid = QGridLayout()
grid.setRowStretch(0, 1)
grid.setRowStretch(1, 2)
grid.addWidget(self.stockWidget, 0, 0, 2, 2)
grid.addWidget(self.extraCharts, 1, 0, 2, 2)
centralWidget = QWidget()
centralWidget.setLayout(grid)
self.setCentralWidget(centralWidget)
class StockChart(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
lay = QVBoxLayout(self)
lay.addWidget(self.chart_view)
#cached_property
def chart_view(self):
chart = QChart()
chart.legend().hide()
chart.createDefaultAxes()
chart.setTitle("/ES Candlestick Chart")
view = QChartView(chart)
view.setRenderHint(QPainter.Antialiasing)
return view
def drawChart(self, data):
series = QCandlestickSeries()
for row in data:
time = row[1] * 1000
open = row[2]
high = row[3]
low = row[4]
close = row[5]
volume = row[6]
series.append(QCandlestickSet(open, high, low, close, time))
self.chart_view.chart().addSeries(series)
class ExtraCharts(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.init_ui()
#cached_property
def views(self):
return list()
def init_ui(self):
grid = QGridLayout(self)
positions = (
(0, 0, "Chart 1"),
(0, 1, "Chart 2"),
(1, 0, "Chart 3"),
(1, 1, "Chart 4"),
)
for (row, column, title) in positions:
view = QChartView()
view.setRenderHint(QPainter.Antialiasing)
view.chart().setTitle(title)
view.chart().legend().hide()
view.chart().createDefaultAxes()
grid.addWidget(view, row, column)
self.views.append(view)
def drawChart(self, data):
for view in self.views:
series = QCandlestickSeries()
view.chart().addSeries(series)
for row in data:
time = row[1] * 1000
open = row[2]
high = row[3]
low = row[4]
close = row[5]
volume = row[6]
series.append(QCandlestickSet(open, high, low, close, time))
if __name__ == "__main__":
app = QApplication([])
db = MySql()
window = MainWindow(db)
window.show()
sys.exit(app.exec())

Related

How change radio button of Radio Button Group in cell of Table programatically?

I want to change the selected radio button in group of radio button in cell table programatically.
I try to recreate table after change the status in piece object but if I have many records it becomes slow.
I have the next code:
import sys
from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QHBoxLayout, QWidget, QButtonGroup, \
QRadioButton, QVBoxLayout, QPushButton, QApplication
class Piece:
def __init__(self, init, name, status):
self.init = init
self.name = name
self.status = status
class Table(QWidget):
def __init__(self, parent=None):
super(Table, self).__init__(parent)
self.debug_data()
self.fila = 0
self.init_ui()
def debug_data(self):
print("okay")
self.pieces: [Piece] = []
self.pieces.append(Piece('2021-01-11 13:52:00', 'Router 234', 1))
self.pieces.append(Piece('2021-01-11 13:55:00', 'Router AB', 0))
self.pieces.append(Piece('2021-01-11 14:00:00', 'Router A234E', 1))
self.pieces.append(Piece('2021-01-11 14:01:00', 'Router Sufle', 0,))
def _create_buttons(self):
self.buttons_layout = QHBoxLayout()
self.btn_ok_all = QPushButton("All Manual")
self.btn_no_ok_all = QPushButton("All Automatic")
self.btn_no_validate_all = QPushButton("All Hybrid")
self.buttons_layout.addWidget(self.btn_ok_all)
self.buttons_layout.addWidget(self.btn_no_ok_all)
self.buttons_layout.addWidget(self.btn_no_validate_all)
self.layout.addLayout(self.buttons_layout)
def init_ui(self):
self.createTable()
self.layout = QVBoxLayout()
self._create_buttons()
self.layout.addWidget(self.tableWidget)
self.setLayout(self.layout)
self.show()
def createTable(self):
self.tableWidget = QTableWidget()
self.tableWidget.setColumnCount(3)
self.tableWidget.setHorizontalHeaderLabels(
["Init Time", "Name", "Type"])
self.process_rows()
def process_rows(self):
for piece in self.pieces:
self.add_row(piece)
def add_row(self, piece):
self.tableWidget.insertRow(self.fila)
self.tableWidget.setItem(self.fila, 0, QTableWidgetItem(piece.init))
self.tableWidget.setItem(self.fila, 1, QTableWidgetItem(piece.name))
self.tableWidget.setCellWidget(self.fila, 2, self.create_group_radio_button(piece))
self.fila = self.fila + 1
def create_group_radio_button(self, piece):
layout = QHBoxLayout()
widget = QWidget(self)
widget.setLayout(layout)
number_group = QButtonGroup(widget)
manual = QRadioButton("Manual")
number_group.addButton(manual)
automatic = QRadioButton("Automatic")
number_group.addButton(automatic)
hybrid = QRadioButton("Hybrid")
number_group.addButton(hybrid)
layout.addWidget(manual)
layout.addWidget(automatic)
layout.addWidget(hybrid)
if piece.status == 0:
hybrid.setChecked(True)
elif piece.status == 1:
manual.setChecked(True)
else:
automatic.setChecked(True)
return widget
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Table()
sys.exit(app.exec_())
What is the way to change one of them programatically?
How to access the cell widget and select the button to change it?
This is a situation for which creating a class is probably the best approach: it improves the object structure and allows creation of functions that give easier access to it.
class PieceSelector(QWidget):
def __init__(self, piece):
super().__init__()
self.piece = piece
layout = QHBoxLayout(self)
self.number_group = QButtonGroup(self)
manual = QRadioButton("Manual", 1)
self.number_group.addButton(manual)
automatic = QRadioButton("Automatic", 2)
self.number_group.addButton(automatic)
hybrid = QRadioButton("Hybrid", 0)
self.number_group.addButton(hybrid)
layout.addWidget(manual)
layout.addWidget(automatic)
layout.addWidget(hybrid)
self.number_group.button(piece.status).setChecked(True)
def get_status(self):
return self.number_group.checkedId()
def set_status(self, status):
self.number_group.button(status).setChecked(True)
Then you just add the widget by creating the instance and get it back using cellWidget():
def add_row(self, piece):
row = self.tableWidget.rowCount()
self.tableWidget.insertRow(row)
self.tableWidget.setItem(row, 0, QTableWidgetItem(piece.init))
self.tableWidget.setItem(row, 1, QTableWidgetItem(piece.name))
self.tableWidget.setCellWidget(row, 2, PieceSelector(piece))
def set_status_for_row(self, row, status):
self.tableWidget.cellWidget(row, 2).set_status(status)

How to plot data from QTableWidget into QChart

I'm working on data analysis software, which takes data from remote database and puts it into QTableWidget. How could I effectively get these data from table and put them into QChart?
I've seen that if I had been using QTableView, it could have been done with models, but as I understand it, using QTableView would be far more complicated for my scenario.
from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtCore import *
from PySide2.QtCharts import *
import sys
import random
class DateTimeDelegate(QStyledItemDelegate):
def initStyleOption(self, option, index):
super(DateTimeDelegate, self).initStyleOption(option, index)
value = index.data()
option.text =
QDateTime.fromMSecsSinceEpoch(value).toString("dd.MM.yyyy")
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setGeometry(0, 0, 1280, 400)
self.chart_table()
self.populate()
def chart_table(self):
self.table = QTableWidget(0, 2)
delegate = DateTimeDelegate(self.table)
self.table.setItemDelegateForColumn(0, delegate)
chart = QtCharts.QChart()
self.chartView = QtCharts.QChartView(chart)
self.chartView.setFixedSize(600, 430)
splitter = QSplitter(self)
splitter.addWidget(self.table)
splitter.addWidget(self.chartView)
self.setCentralWidget(splitter)
series = QtCharts.QLineSeries(name='Odoslané')
mapper = QtCharts.QVXYModelMapper(xColumn=0, yColumn=2)
mapper.setModel(self.table.model())
mapper.setSeries(series)
chart.addSeries(mapper.series())
self.axis_X = QtCharts.QDateTimeAxis()
self.axis_X.setFormat("MMM yyyy")
self.axis_Y = QtCharts.QValueAxis()
chart.setAxisX(self.axis_X, series)
chart.setAxisY(self.axis_Y, series)
self.axis_Y.setRange(0, 0)
self.axis_Y.setLabelFormat('%.0f')
self.axis_X.setRange(QDate(2017, 10, 1), QDate.currentDate())
chart.setTitle('Chart')
def addRow(self, dt, value):
self.table.insertRow(0)
for col, v in enumerate((dt.toMSecsSinceEpoch(), value)):
it = QTableWidgetItem()
it.setData(Qt.DisplayRole, dt.toMSecsSinceEpoch())
self.table.setItem(0, 0, it)
t_m, t_M = self.axis_X.min(), self.axis_X.max()
t_m = min(t_m, dt)
t_M = max(t_M, dt)
m, M = self.axis_Y.min(), self.axis_Y.max()
m = min(m, value)
M = max(M, value)
In this method I simulate filling table with data as I get them from database.
def populate(self):
for i in range(4):
count=random.randint(1,40)
value_str = QDate.currentDate().addDays(count).toString('dd.MM.yyyy')
dt = QDateTime.fromString(value_str, "dd.MM.yyyy")
sent = QTableWidgetItem(str(count))
value = int(sent.text())
self.addRow(dt, value)
self.table.setItem(0, 1, sent)
And App running function -
def main():
app = QApplication(sys.argv)
gui = MainWindow()
gui.show()
sys.exit(app.exec_())
main()
The easiest way to show the data of a QTableWidget in a QChartView is to use a QVXYModelMapper that relates the model of the QTableWidget with a QLineSerie. But for this the data stored in the QTableWidget should not be a string but an integer so you should not convert the QDateTime to string using toString(), but to an integer using toMSecsSinceEpoch(), and to show it as datetime in the QTableWidget a delegate should be used.
In the following example the addRow method allows to add a (QDateTime, value) to a row, this recalculates the ranges of each axis.
import random
from functools import partial
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCharts import QtCharts
class DateTimeDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super(DateTimeDelegate, self).initStyleOption(option, index)
value = index.data()
option.text = QtCore.QDateTime.fromMSecsSinceEpoch(value).toString("dd.MM.yyyy")
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.m_tablewidget = QtWidgets.QTableWidget(0, 2)
delegate = DateTimeDelegate(self.m_tablewidget)
self.m_tablewidget.setItemDelegateForColumn(0, delegate)
self.m_chartview = QtCharts.QChartView()
self.m_chartview.chart().setTheme(QtCharts.QChart.ChartThemeQt)
self.m_chartview.setMinimumWidth(400)
self.m_series = QtCharts.QLineSeries(name="Time-Value")
self.m_mapper = QtCharts.QVXYModelMapper(self, xColumn=0, yColumn=1)
self.m_mapper.setModel(self.m_tablewidget.model())
self.m_mapper.setSeries(self.m_series)
self.m_chartview.chart().addSeries(self.m_mapper.series())
splitter = QtWidgets.QSplitter(self)
splitter.addWidget(self.m_tablewidget)
splitter.addWidget(self.m_chartview)
self.setCentralWidget(splitter)
self.m_time_axis = QtCharts.QDateTimeAxis()
self.m_time_axis.setFormat("dd.MM.yyyy")
self.m_value_axis = QtCharts.QValueAxis()
self.m_chartview.chart().setAxisX(self.m_time_axis, self.m_series)
self.m_chartview.chart().setAxisY(self.m_value_axis, self.m_series)
self.m_value_axis.setRange(0, 0)
self.m_time_axis.setRange(
QtCore.QDateTime.currentDateTime(),
QtCore.QDateTime.currentDateTime().addDays(1),
)
def addRow(self, dt, value):
row = self.m_tablewidget.rowCount()
self.m_tablewidget.insertRow(row)
for col, v in enumerate((dt.toMSecsSinceEpoch(), value)):
it = QtWidgets.QTableWidgetItem()
it.setData(QtCore.Qt.DisplayRole, v)
self.m_tablewidget.setItem(row, col, it)
t_m, t_M = self.m_time_axis.min(), self.m_time_axis.max()
t_m = min(t_m, dt)
t_M = max(t_M, dt)
m, M = self.m_value_axis.min(), self.m_value_axis.max()
m = min(m, value)
M = max(M, value)
self.m_time_axis.setRange(t_m, t_M)
self.m_value_axis.setRange(m, M)
counter = 0
def onTimeout(w):
# Emulate the data
global counter
dt = QtCore.QDateTime.currentDateTime().addDays(counter)
value = random.uniform(-100, 100)
w.addRow(dt, value)
counter += 1
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
wrapper = partial(onTimeout, w)
timer = QtCore.QTimer(timeout=wrapper, interval=1000)
timer.start()
sys.exit(app.exec_())
Update:
You do not have to create any QTableWidget in the populate method. I have corrected your logic so that it is added to the top of the QTableWidget, also I have corrected the calculation of the range.
import sys
import random
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtCharts import QtCharts
class DateTimeDelegate(QStyledItemDelegate):
def initStyleOption(self, option, index):
super(DateTimeDelegate, self).initStyleOption(option, index)
value = index.data()
option.text = QDateTime.fromMSecsSinceEpoch(value).toString("dd.MM.yyyy")
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setGeometry(0, 0, 1280, 400)
self.chart_table()
self.populate()
def chart_table(self):
self.table = QTableWidget(0, 2)
delegate = DateTimeDelegate(self.table)
self.table.setItemDelegateForColumn(0, delegate)
chart = QtCharts.QChart()
self.chartView = QtCharts.QChartView(chart)
self.chartView.setFixedSize(600, 430)
splitter = QSplitter(self)
splitter.addWidget(self.table)
splitter.addWidget(self.chartView)
self.setCentralWidget(splitter)
series = QtCharts.QLineSeries(name="Odoslané")
mapper = QtCharts.QVXYModelMapper(self, xColumn=0, yColumn=1)
mapper.setModel(self.table.model())
mapper.setSeries(series)
chart.addSeries(mapper.series())
self.axis_X = QtCharts.QDateTimeAxis()
self.axis_X.setFormat("MMM yyyy")
self.axis_Y = QtCharts.QValueAxis()
chart.setAxisX(self.axis_X, series)
chart.setAxisY(self.axis_Y, series)
self.axis_Y.setRange(0, 0)
self.axis_Y.setLabelFormat("%.0f")
chart.setTitle("Chart")
def addRow(self, dt, value):
self.table.insertRow(0)
for col, v in enumerate((dt.toMSecsSinceEpoch(), value)):
it = QTableWidgetItem()
it.setData(Qt.DisplayRole, v)
self.table.setItem(0, col, it)
if self.table.rowCount() == 1:
self.axis_X.setRange(dt, dt.addDays(1))
self.axis_Y.setRange(v, v)
else:
t_m, t_M = self.axis_X.min(), self.axis_X.max()
t_m = min(t_m, dt)
t_M = max(t_M, dt)
m, M = self.axis_Y.min(), self.axis_Y.max()
m = min(m, value)
M = max(M, value)
self.axis_X.setRange(t_m, t_M)
self.axis_Y.setRange(m, M)
def populate(self):
for i in range(100):
# simulate filling table with data as I get them from database.
value = random.uniform(1, 40)
fake_dt_str = QDate.currentDate().addDays(i).toString("dd.MM.yyyy")
fake_value_str = str(random.uniform(0, 2))
# Convert simulated data
dt = QDateTime.fromString(fake_dt_str, "dd.MM.yyyy")
value = float(fake_value_str)
self.addRow(dt, value)
def main():
app = QApplication(sys.argv)
gui = MainWindow()
gui.show()
sys.exit(app.exec_())
main()

How to add dropdown menu to QMessageBox?

After looking at some code I found on stackoverflow, I was able to find a way to add a table to a QmessageBox. Now that I have done that, I would like to place a drop down menu in the top right of the QmessageBox and I cannot figure out a way to do that (if it even is possible).
Here is my edited code:
from PyQt4.QtGui import *
from PyQt4.Qt import *
import sys
class MyMessageBox(QMessageBox):
def __init__(self):
QMessageBox.__init__(self)
self.setSizeGripEnabled (True)
self.setWindowTitle('Get Parent Script')
self.setIcon(self.Question)
#self.setText("Hello MessageBox")
self.addButton("Select", QMessageBox.ActionRole)
self.setStandardButtons(QMessageBox.Cancel)
#self.addWidget(QInputDialog())
self.addTableWidget (self)
currentClick = self.exec_()
def addTableWidget (self, parentItem) :
self.l = QVBoxLayout()
self.tableWidget = QTableWidget(parentItem)
self.tableWidget.setObjectName ('tableWidget')
self.tableWidget.setColumnCount(3)
self.tableWidget.setRowCount(2)
self.tableWidget.setHorizontalHeaderLabels(QString("Nuke Script;File Modification Time;User").split(";"))
header = self.tableWidget.horizontalHeader()
header.setResizeMode(0, QHeaderView.ResizeToContents)
header.setResizeMode(1, QHeaderView.Stretch)
header.setResizeMode(2, QHeaderView.Stretch)
stringlist = {u'/SEQ/ZZ/ZZ_012_001/Comp/nuke/scripts/comp':u'user1', u'/SEQ/ZZ/ZZ_012_001/Comp/nuke/scripts/comp/hello': u'user2'}
row = 0
for key, value in stringlist.iteritems():
print key, value
nameitem = QTableWidgetItem(str(key))
codeitem = QTableWidgetItem(str(value))
self.tableWidget.setItem(row,0,nameitem)
self.tableWidget.setItem(row,1,codeitem)
row +=1
self.tableWidget.resize(1000, 170)
self.l.addWidget(self.tableWidget)
self.setLayout(self.l)
def event(self, e):
result = QMessageBox.event(self, e)
self.setMinimumWidth(0)
self.setMaximumWidth(16777215)
self.setMinimumHeight(0)
self.setMaximumHeight(16777215)
self.setSizePolicy(
QSizePolicy.Expanding,
QSizePolicy.Expanding
)
self.resize(1000, 300)
return result
def run_cli():
#app = QtWidgets.QApplication(sys.argv)
app = QApplication(sys.argv)
MyMessageBox()
if __name__ == '__main__':
run_cli()
In your case it is not optimal to use QMessageBox since I involve unnecessary work because this widget already has a predefined layout, instead you can create a widget based on a QDialog:
from PyQt4 import QtCore, QtGui
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
label = QtGui.QLabel("Text")
combo = QtGui.QComboBox()
combo.addItems(["option1", "option2", "option3"])
self.tableWidget = QtGui.QTableWidget(2, 3)
self.tableWidget.setHorizontalHeaderLabels(
QtCore.QString("Nuke Script;File Modification Time;User").split(";")
)
header = self.tableWidget.horizontalHeader()
header.setResizeMode(0, QtGui.QHeaderView.ResizeToContents)
header.setResizeMode(1, QtGui.QHeaderView.Stretch)
header.setResizeMode(2, QtGui.QHeaderView.Stretch)
stringlist = {
u"/SEQ/ZZ/ZZ_012_001/Comp/nuke/scripts/comp": u"user1",
u"/SEQ/ZZ/ZZ_012_001/Comp/nuke/scripts/comp/hello": u"user2",
}
for row, (key, value) in enumerate(stringlist.iteritems()):
nameitem = QtGui.QTableWidgetItem(str(key))
codeitem = QtGui.QTableWidgetItem(str(value))
self.tableWidget.setItem(row, 0, nameitem)
self.tableWidget.setItem(row, 1, codeitem)
box = QtGui.QDialogButtonBox(
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel,
centerButtons=True,
)
box.accepted.connect(self.accept)
box.rejected.connect(self.reject)
lay = QtGui.QGridLayout(self)
lay.addWidget(label, 0, 0)
lay.addWidget(combo, 0, 1)
lay.addWidget(self.tableWidget, 1, 0, 1, 2)
lay.addWidget(box, 2, 0, 1, 2)
self.resize(640, 240)
def run_cli():
import sys
app = QtGui.QApplication(sys.argv)
w = Dialog()
w.exec_()
if __name__ == "__main__":
run_cli()

Why QTableWidgetItem doesn't keep updating it's color?

I have a QTableWidget in my program. I want it's rows and columns to change color according to the time entry in the table widget. When I first add the time, it's compared to current system time and the color changing does take effect but as time goes by it doesn't keep up and the colors remain the same. I want it to keep updating table item's color. I cant include all the code as it is too long but I have included the relevant part.
#Operations_Tree is the QTABLEWIDGET
def Scheduler_Loop(self):
Timer = QtCore.QTimer(self)
Timer.start(1000)
Timer.timeout.connect(self.test)
def test(self):
time = QtCore.QTime.currentTime()
current_time_text = time.toString('hh:mm:ss')
for row in range(0,self.Operations_Tree.rowCount()):
time = self.Operations_Tree.item(row,1)
time_text = time.text()
#print(time_text)
if time_text >= current_time_text :
for column in range(0,6):
#print("TEST")
try:
table_widget_item = self.Operations_Tree.item(row, column)
table_widget_item.setForeground(QtGui.QColor(0,0,255)) #BLue
table_widget_item.setBackground(QtGui.QColor(238,233,233)) #
table_widget_item.viewport.update()
except AttributeError:
pass
else:
for column in range(0,6):
#print("TEST")
try:
table_widget_item = self.Operations_Tree.item(row, column)
#table_widget_item.setForeground(QtGui.QColor(0,255,0)) #
table_widget_item.setBackground(QtGui.QColor(238,233,233)) #
table_widget_item.viewport.update()
except AttributeError:
pass
self.Operations_Tree.update()
here is a snap of my program:
For me the main problem is that you are comparing strings instead of time, also in Qt it is not necessary to use try-except since many times they hide errors and waste unnecessary resources, it is better to verify.
PyQt5:
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.lcd_number = QtWidgets.QLCDNumber()
self.lcd_number.setFrameShape(QtWidgets.QFrame.NoFrame)
self.lcd_number.setFixedHeight(100)
self.lcd_number.setDigitCount(8)
self.Operations_Tree = QtWidgets.QTableWidget()
self.Operations_Tree.setColumnCount(6)
labels = ["On AIR", "TIME", "ITEM", "DURATION", "B.ITEM", "B.I.DURATION"]
self.Operations_Tree.setHorizontalHeaderLabels(labels)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.lcd_number)
lay.addWidget(self.Operations_Tree)
for i, t in enumerate(("10:10:10", "12:12:12")):
self.Operations_Tree.insertRow(self.Operations_Tree.rowCount())
it = QtWidgets.QTableWidgetItem(t)
self.Operations_Tree.setItem(i, 1, it)
self.resize(640, 480)
self.Scheduler_Loop()
def Scheduler_Loop(self):
Timer = QtCore.QTimer(self)
Timer.timeout.connect(self.test)
Timer.start(1000)
self.test()
#QtCore.pyqtSlot()
def test(self):
time = QtCore.QTime.currentTime()
self.lcd_number.display(time.toString())
for row in range(self.Operations_Tree.rowCount()):
it = self.Operations_Tree.item(row, 1)
r_time = QtCore.QTime.fromString(it.text(), "h:mm:ss")
flag = r_time >= time
f_color = QtGui.QColor(0, 0, 255) if flag else QtGui.QColor(0, 255, 0)
b_color = QtGui.QColor(238, 233, 233) if flag else QtGui.QColor(238,233,233)
for column in range(0, 6):
it = self.Operations_Tree.item(row, column)
if it is None:
it = QtWidgets.QTableWidgetItem()
self.Operations_Tree.setItem(row, column, it)
it.setForeground(f_color)
it.setBackground(b_color)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
PyQt4:
from PyQt4 import QtCore, QtGui
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.lcd_number = QtGui.QLCDNumber()
self.lcd_number.setFrameShape(QtGui.QFrame.NoFrame)
self.lcd_number.setFixedHeight(100)
self.lcd_number.setDigitCount(8)
self.Operations_Tree = QtGui.QTableWidget()
self.Operations_Tree.setColumnCount(6)
labels = ["On AIR", "TIME", "ITEM", "DURATION", "B.ITEM", "B.I.DURATION"]
self.Operations_Tree.setHorizontalHeaderLabels(labels)
lay = QtGui.QVBoxLayout(self)
lay.addWidget(self.lcd_number)
lay.addWidget(self.Operations_Tree)
for i, t in enumerate(("10:10:10", "12:12:12")):
self.Operations_Tree.insertRow(self.Operations_Tree.rowCount())
it = QtGui.QTableWidgetItem(t)
self.Operations_Tree.setItem(i, 1, it)
self.resize(640, 480)
self.Scheduler_Loop()
def Scheduler_Loop(self):
Timer = QtCore.QTimer(self)
Timer.timeout.connect(self.test)
Timer.start(1000)
self.test()
#QtCore.pyqtSlot()
def test(self):
time = QtCore.QTime.currentTime()
self.lcd_number.display(time.toString())
for row in range(self.Operations_Tree.rowCount()):
it = self.Operations_Tree.item(row, 1)
r_time = QtCore.QTime.fromString(it.text(), "h:mm:ss")
flag = r_time >= time
f_color = QtGui.QColor(0, 0, 255) if flag else QtGui.QColor(0, 255, 0)
b_color = QtGui.QColor(238, 233, 233) if flag else QtGui.QColor(238,233,233)
for column in range(0, 6):
it = self.Operations_Tree.item(row, column)
if it is None:
it = QtGui.QTableWidgetItem()
self.Operations_Tree.setItem(row, column, it)
it.setForeground(f_color)
it.setBackground(b_color)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

Add combobox to horizontal header in QTableWidget

I want to create a table in PyQt5 that has a combobox in each column header. When I try to do it, the following error is returned:
TypeError: setHorizontalHeaderItem(self, int, QTableWidgetItem): argument 2 has unexpected type 'QComboBox'
Apparently the function setHorizontalHeaderItem() doesn't accept widgets as items. So is there a way to achieve this? If not, I would settle with putting the comboboxes above the headers, but they should be aligned with the size of each column, even if the user changes the width with the mouse. I don't know if this is possible either.
My code:
from PyQt5 import QtWidgets
import numpy as np
class App(QtWidgets.QWidget):
def __init__(self):
super(App,self).__init__()
self.data = np.random.rand(5,5)
self.createTable()
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.table)
self.setLayout(self.layout)
self.showMaximized()
def createTable(self):
self.header = []
self.table = QtWidgets.QTableWidget(len(self.data), len(self.data[0]))
for i in range(len(self.data[0])):
self.header.append(QtWidgets.QComboBox())
self.header[-1].addItem('Variable')
self.header[-1].addItem('Timestamp')
self.table.setHorizontalHeaderItem(i,self.header[-1])
for i in range(len(self.data)):
for j in range(len(self.data[0])):
self.table.setItem(i,j,QtWidgets.QTableWidgetItem(str(self.data[i][j])))
if __name__ == '__main__':
app = QtWidgets.QApplication([])
ex = App()
app.exec_()
QHeaderView does not support widgets as items so you must create a custom header as I show below:
from PyQt5 import QtCore, QtWidgets
import numpy as np
class HorizontalHeader(QtWidgets.QHeaderView):
def __init__(self, values, parent=None):
super(HorizontalHeader, self).__init__(QtCore.Qt.Horizontal, parent)
self.setSectionsMovable(True)
self.comboboxes = []
self.sectionResized.connect(self.handleSectionResized)
self.sectionMoved.connect(self.handleSectionMoved)
def showEvent(self, event):
for i in range(self.count()):
if i < len(self.comboboxes):
combo = self.comboboxes[i]
combo.clear()
combo.addItems(["Variable", "Timestamp"])
else:
combo = QtWidgets.QComboBox(self)
combo.addItems(["Variable", "Timestamp"])
self.comboboxes.append(combo)
combo.setGeometry(self.sectionViewportPosition(i), 0, self.sectionSize(i)-4, self.height())
combo.show()
if len(self.comboboxes) > self.count():
for i in range(self.count(), len(self.comboboxes)):
self.comboboxes[i].deleteLater()
super(HorizontalHeader, self).showEvent(event)
def handleSectionResized(self, i):
for i in range(self.count()):
j = self.visualIndex(i)
logical = self.logicalIndex(j)
self.comboboxes[i].setGeometry(self.sectionViewportPosition(logical), 0, self.sectionSize(logical)-4, self.height())
def handleSectionMoved(self, i, oldVisualIndex, newVisualIndex):
for i in range(min(oldVisualIndex, newVisualIndex), self.count()):
logical = self.logicalIndex(i)
self.comboboxes[i].setGeometry(self.ectionViewportPosition(logical), 0, self.sectionSize(logical) - 5, height())
def fixComboPositions(self):
for i in range(self.count()):
self.comboboxes[i].setGeometry(self.sectionViewportPosition(i), 0, self.sectionSize(i) - 5, self.height())
class TableWidget(QtWidgets.QTableWidget):
def __init__(self, *args, **kwargs):
super(TableWidget, self).__init__(*args, **kwargs)
header = HorizontalHeader(self)
self.setHorizontalHeader(header)
def scrollContentsBy(self, dx, dy):
super(TableWidget, self).scrollContentsBy(dx, dy)
if dx != 0:
self.horizontalHeader().fixComboPositions()
class App(QtWidgets.QWidget):
def __init__(self):
super(App,self).__init__()
self.data = np.random.rand(10, 10)
self.createTable()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.table)
self.showMaximized()
def createTable(self):
self.header = []
self.table = TableWidget(*self.data.shape)
for i, row_values in enumerate(self.data):
for j, value in enumerate(row_values):
self.table.setItem(i, j, QtWidgets.QTableWidgetItem(str(value)))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())

Categories

Resources