I have created a table in PyQt5 and and retrieve the data in the cell by this method. But when i try the cell in QTableWidgetItem becomes None. So, how i can solve this problem and how i can insert other determined items in a specifice cell for exemple item 2, item3,... Thank you
import sys
from PyQt5.QtWidgets import (QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem)
from PyQt5.QtGui import QBrush, QColor
from PyQt5 import QtCore
data = {'111':['Title 1','121','94565','','','','684651','','','44651','','',''], '112':['Title 2','65115','','466149','46645','555641','','','','412045','98416','',''], '113':['Title 3','','','','466149','46645','555641','98656','','','412045','98416','','']}
class Table(QWidget):
def __init__(self, *args, parent=None):
super().__init__()
self.data = data
self.setuptUI()
k = 'str'
v = int
n = int
m = int
self.setdata(k, v, n, m)
def setuptUI(self):
self.setWindowTitle("QTableWidgetItem")
self.resize(1200, 800)
conLayout = QHBoxLayout()
self.tableWidget =QTableWidget(self)
self.tableWidget.setRowCount(55)
self.tableWidget.setColumnCount(14)
conLayout.addWidget(self.tableWidget)
def setdata(self, k, v, n, m):
global item
item = str(self.data.get(k))
for key in self.data:
if k in key:
item = self.data.get(k)[v]
print(item)
newItem = QTableWidgetItem(str(item))
newItem.setForeground(QBrush(QColor(255, 0, 0)))
self.tableWidget.setItem(23, 4, newItem)
if __name__ == '__main__':
app = QApplication(sys.argv)
windows = Table(data)
item1= Table()
item1.setdata(k="113", v=5, n=24, m=4)
#item2= Table()
#item2.setdata(k="113", v=5, n=25, m=4
windows.show()
sys.exit(app.exec_())
Try it:
import sys
from PyQt5.QtWidgets import (QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem)
from PyQt5.QtGui import QBrush, QColor
from PyQt5 import QtCore
data = {'111':['Title 1','121','94565','','','','684651','','','44651','','',''],
'112':['Title 2','65115','','466149','46645','555641','','','','412045','98416','',''],
'113':['Title 3','','','','466149','46645','555641','98656','','','412045','98416','','']}
class Table(QWidget):
def __init__(self, data): # data
super().__init__()
self.data = data
self.setuptUI()
# k = 'str'
# v = int
# n = int
# m = int
# self.setdata(k, v, n, m)
def setuptUI(self):
self.setWindowTitle("QTableWidgetItem")
self.resize(1200, 600)
conLayout = QHBoxLayout(self) # + self
self.tableWidget = QTableWidget(self)
self.tableWidget.setRowCount(55)
self.tableWidget.setColumnCount(14)
conLayout.addWidget(self.tableWidget)
def setdata(self, k, v, n, m):
# global item # ---
# item = str(self.data.get(k))
# for key in self.data:
# if k in key:
item = self.data.get(k)[v]
print(item)
newItem = QTableWidgetItem(str(item))
newItem.setForeground(QBrush(QColor(255, 0, 0)))
# self.tableWidget.setItem(23, 4, newItem)
self.tableWidget.setItem(n, m, newItem)
if __name__ == '__main__':
app = QApplication(sys.argv)
windows = Table(data)
#- item1= Table()
#- item1.setdata(k="113", v=5, n=24, m=4)
windows.setdata(k="113", v=5, n=24, m=4)
windows.setdata(k="113", v=6, n=24, m=5) # v=6, m=5
windows.show()
sys.exit(app.exec_())
Related
how can I get audio input in real time from QAudioInput, store it in a NumPy array and pass it to SciPy FFT? What I have tried:
from PyQt5.QtMultimedia import QAudioDeviceInfo, QAudioFormat, QAudioInput
import sys
class Window(QMainWindow):
def __init__(self):
info = QAudioDeviceInfo()
input_device = info.defaultInputDevice()
if input_device.isNull():
# If no avaiable device is found, we display a error
print("There is no audio input device available.")
exit(-1)
audio_format = QAudioFormat()
audio_format.setSampleRate(44100)
audio_format.setSampleSize(8)
audio_format.setChannelCount(1)
audio_format.setCodec("audio/pcm")
audio_format.setSampleType(QAudioFormat.UnSignedInt)
if sys.byteorder == "little":
audio_format.setByteOrder(QAudioFormat.LittleEndian)
else:
audio_format.setByteOrder(QAudioFormat.BigEndian)
self.audioInput = QAudioInput(input_device, audio_format, self)
self.ioDevice = self.audioInput.start()
self.ioDevice.readyRead.connect(self.read_audio)
def read_audio(self):
data: QByteArray = self.ioDevice.readAll()
print(data.toUInt()) # Prints (0, False) which means error converting data
Inspired by the official example Audio Example I have created a QIODevice that allows obtaining the data. The following example takes the last N samples every T seconds by calculating its fft and displaying it using matplotlib.
import sys
import collections
from functools import cached_property
from PyQt5.QtCore import QIODevice, QObject, pyqtSignal, QTimer
from PyQt5.QtMultimedia import QAudioDeviceInfo, QAudioFormat, QAudioInput
from PyQt5.QtWidgets import QApplication, QMainWindow
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
from scipy.fft import fft, fftfreq
import numpy as np
FS = 44100
SAMPLE_COUNT = 2 * 1000
class AudioDevice(QIODevice):
data_changed = pyqtSignal(list, name="dataChanged")
def __init__(self, interval=1000, parent: QObject = None):
super().__init__(parent)
self.m_buffer = collections.deque(
[0 for _ in range(SAMPLE_COUNT)], maxlen=SAMPLE_COUNT
)
self.timer.timeout.connect(self.send_data)
self.timer.setInterval(interval)
self.timer.start()
#cached_property
def timer(self):
return QTimer()
def send_data(self):
self.data_changed.emit(list(self.m_buffer))
def readData(self, data, max_size):
return -1
def writeData(self, data):
max_size = len(data)
resolution = 4
start = 0
available_samples = int(max_size) // resolution
if available_samples < self.m_buffer.maxlen:
start = self.m_buffer.maxlen - available_samples
pos = 0
for _ in range(start, self.m_buffer.maxlen):
y = (1.0 * (data[pos] - 128)) / 128.0
self.m_buffer.append(y)
pos += resolution
return (self.m_buffer.maxlen - start) * resolution
class PlotWidget(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.canvas = FigureCanvas(Figure(figsize=(5, 3)))
self.setCentralWidget(self.canvas)
self.ax = self.canvas.figure.subplots()
self._line = None
def update_data(self, data):
T = 1 / FS
N = SAMPLE_COUNT
yf = fft(data)
xf = fftfreq(N, T)[: N // 2]
x = xf
y = 2.0 / N * np.abs(yf[0 : N // 2])
if self._line is None:
(self._line,) = self.ax.plot(x, y)
else:
self._line.set_data(x, y)
self.canvas.draw()
def main(args):
app = QApplication(args)
plot_widget = PlotWidget()
plot_widget.resize(640, 480)
plot_widget.show()
info = QAudioDeviceInfo()
input_device = info.defaultInputDevice()
if input_device.isNull():
print("There is no audio input device available.")
exit(-1)
audio_format = QAudioFormat()
audio_format.setSampleRate(FS)
audio_format.setSampleSize(8)
audio_format.setChannelCount(1)
audio_format.setCodec("audio/pcm")
audio_format.setSampleType(QAudioFormat.UnSignedInt)
if sys.byteorder == "little":
audio_format.setByteOrder(QAudioFormat.LittleEndian)
else:
audio_format.setByteOrder(QAudioFormat.BigEndian)
audio_input = QAudioInput(input_device, audio_format, None)
audio_device = AudioDevice(interval=100)
audio_device.data_changed.connect(plot_widget.update_data)
audio_device.open(QIODevice.WriteOnly)
audio_input.start(audio_device)
app.exec_()
if __name__ == "__main__":
main(sys.argv)
data.toUInt() converts whole byte array to one uint value - not what you want. To get sample values you can use either numpy.frombuffer or struct.unpack.
import numpy
def read_audio(self):
data = self.ioDevice.readAll()
values = numpy.frombuffer(data.data(), dtype=numpy.uint8)
or
import struct
def read_audio(self):
data = self.ioDevice.readAll()
fmt = "#{}B".format(data.size())
values = struct.unpack(fmt, data.data())
I added widget that shows waveform to demonstrate that samples actually reflect signal from microphone - not random numbers.
from PyQt5.QtMultimedia import QAudioDeviceInfo, QAudioFormat, QAudioInput
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget
from PyQt5.QtGui import QPainter, QPolygonF
from PyQt5.QtCore import QPointF
import sys
import numpy
class WaveWidget(QWidget):
def __init__(self, parent = None):
super().__init__(parent)
self._values = None
def setValues(self, values):
self._values = values
self.update()
def paintEvent(self, event):
if self._values is None:
return
painter = QPainter(self)
ys = self._values / 255 * self.height()
xs = numpy.linspace(0, self.width(), num = len(ys))
points = QPolygonF([QPointF(x,y) for x,y in zip(xs,ys)])
painter.drawPolyline(points)
class Window(QMainWindow):
def __init__(self):
super().__init__()
info = QAudioDeviceInfo()
input_device = info.defaultInputDevice()
if input_device.isNull():
# If no avaiable device is found, we display a error
print("There is no audio input device available.")
exit(-1)
audio_format = QAudioFormat()
audio_format.setSampleRate(44100)
audio_format.setSampleSize(8)
audio_format.setChannelCount(1)
audio_format.setCodec("audio/pcm")
audio_format.setSampleType(QAudioFormat.UnSignedInt)
if sys.byteorder == "little":
audio_format.setByteOrder(QAudioFormat.LittleEndian)
else:
audio_format.setByteOrder(QAudioFormat.BigEndian)
self.audioInput = QAudioInput(input_device, audio_format, self)
self.ioDevice = self.audioInput.start()
self.ioDevice.readyRead.connect(self.read_audio)
widget = WaveWidget()
self._widget = widget
self.setCentralWidget(widget)
def read_audio(self):
data = self.ioDevice.readAll()
values = numpy.frombuffer(data.data(), dtype=numpy.uint8)
self._widget.setValues(values)
if __name__ == "__main__":
app = QApplication([])
window = Window()
window.show()
app.exec()
Hello Experts!! I hope you are having great day. I am new in GUI programming specially PyQt5. I am practicing on simple GUI invoice application. In this application, I successfully generated the Invoice By QTextDocument. Now i want to add print dialogue and print preview option. I am having trouble in the code. This is saying
AttributeError: 'InvoiceForm' object has no attribute
'printpreviewDialog
As i am new, i am little bit confused in there. Could you please fix the code? That will help me a lot to study. Many Many Thanks.
The code has given below:-
import sys
from PyQt5.QtCore import pyqtSignal, QSize, QSizeF, QDate
from PyQt5.QtGui import QTextDocument, QTextCursor, QFont
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
from PyQt5.QtWidgets import QWidget, QFormLayout, QLineEdit, QPlainTextEdit, QSpinBox, QDateEdit, QTableWidget, \
QHeaderView, QPushButton, QHBoxLayout, QTextEdit, QApplication, QMainWindow
font= QFont('Arial',16)
class InvoiceForm(QWidget):
submitted = pyqtSignal(dict)
def __init__(self):
super().__init__()
self.setLayout(QFormLayout())
self.inputs = dict()
self.inputs['Customer Name'] = QLineEdit()
self.inputs['Customer Address'] = QPlainTextEdit()
self.inputs['Invoice Date'] = QDateEdit(date=QDate.currentDate(), calendarPopup=True)
self.inputs['Days until Due'] = QSpinBox()
for label, widget in self.inputs.items():
self.layout().addRow(label, widget)
self.line_items = QTableWidget(rowCount=10, columnCount=3)
self.line_items.setHorizontalHeaderLabels(['Job', 'Rate', 'Hours'])
self.line_items.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.layout().addRow(self.line_items)
for row in range(self.line_items.rowCount()):
for col in range(self.line_items.columnCount()):
if col > 0:
w = QSpinBox()
self.line_items.setCellWidget(row, col, w)
submit = QPushButton('Create Invoice', clicked=self.on_submit)
print = QPushButton('Print Invoice', clicked=self.printpreviewDialog)
self.layout().addRow(submit,print)
def on_submit(self):
data = {'c_name': self.inputs['Customer Name'].text(),
'c_addr': self.inputs['Customer Address'].toPlainText(),
'i_date': self.inputs['Invoice Date'].date().toString(),
'i_due': self.inputs['Invoice Date'].date().addDays(self.inputs['Days until Due'].value()).toString(),
'i_terms': '{} days'.format(self.inputs['Days until Due'].value()),
'line_items': list()}
for row in range(self.line_items.rowCount()):
if not self.line_items.item(row, 0):
continue
job = self.line_items.item(row, 0).text()
rate = self.line_items.cellWidget(row, 1).value()
hours = self.line_items.cellWidget(row, 2).value()
total = rate * hours
row_data = [job, rate, hours, total]
if any(row_data):
data['line_items'].append(row_data)
data['total_due'] = sum(x[3] for x in data['line_items'])
self.submitted.emit(data)
# remove everything else in this function below this point
class InvoiceView(QTextEdit):
dpi = 72
doc_width = 8.5 * dpi
doc_height = 6 * dpi
def __init__(self):
super().__init__(readOnly=True)
self.setFixedSize(QSize(self.doc_width, self.doc_height))
def build_invoice(self, data):
document = QTextDocument()
self.setDocument(document)
document.setPageSize(QSizeF(self.doc_width, self.doc_height))
document.setDefaultFont(font)
cursor = QTextCursor(document)
cursor.insertText(f"Customer Name: {data['c_name']}\n")
cursor.insertText(f"Customer Address: {data['c_addr']}\n")
cursor.insertText(f"Date: {data['i_date']}\n")
cursor.insertText(f"Total Due: {data['total_due']}\n")
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
central = QWidget()
self.setCentralWidget(central)
layout = QHBoxLayout(central)
self.invoiceForm = InvoiceForm()
layout.addWidget(self.invoiceForm)
self.invoiceView = InvoiceView()
layout.addWidget(self.invoiceView)
# hide the widget right now...
self.invoiceView.setVisible(False)
self.invoiceForm.submitted.connect(self.showPreview)
def showPreview(self, data):
self.invoiceView.setVisible(True)
self.invoiceView.build_invoice(data)
def printpreviewDialog(self):
printer = QPrinter(QPrinter.HighResolution)
previewDialog = QPrintPreviewDialog(printer, self)
previewDialog.paintRequested.connect(self.printPreview)
previewDialog.exec_()
def printPreview(self, printer):
self.invoiceView.build_invoice.print_(printer)
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
if __name__ == '__main__':
main()
The main problem is that self.printpreviewDialog is a member of MainWindow, not of InvoiceForm, so you should connect the clicked signal from the main window instead.
Also note that you tried to use self.invoiceView.build_invoice.print_(), but this wouldn't work as you are not calling build_invoice, and even if you did, that function doesn't return anything.
You should use the self.invoiceView.document() instead, but you must ensure that the data has been built before.
class InvoiceForm(QWidget):
submitted = pyqtSignal(dict)
def __init__(self):
# ...
submit = QPushButton('Create Invoice', clicked=self.on_submit)
# make the button a member of the instance instead of a local variable,
# so that we can connect from the main window instance
self.printButton = QPushButton('Print Invoice')
self.layout().addRow(submit, self.printButton)
# ...
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.invoiceForm.printButton.clicked.connect(self.printpreviewDialog)
# ...
def printPreview(self, printer):
self.invoiceView.document().print_(printer)
Note: never, never use built-in functions and statements for variable names, like print.
Try it:
import sys
from PyQt5.QtCore import pyqtSignal, QSize, QSizeF, QDate
from PyQt5.QtGui import QTextDocument, QTextCursor, QFont
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
from PyQt5.QtWidgets import (QWidget, QFormLayout, QLineEdit, QPlainTextEdit,
QSpinBox, QDateEdit, QTableWidget, QHeaderView, QPushButton, QHBoxLayout,
QTextEdit, QApplication, QMainWindow)
font = QFont('Arial',16)
class InvoiceForm(QWidget):
submitted = pyqtSignal(dict)
def __init__(self, parent=None): # + parent=None
super().__init__(parent) # + parent
self.setLayout(QFormLayout())
self.inputs = dict()
self.inputs['Customer Name'] = QLineEdit()
self.inputs['Customer Address'] = QPlainTextEdit()
self.inputs['Invoice Date'] = QDateEdit(date=QDate.currentDate(), calendarPopup=True)
self.inputs['Days until Due'] = QSpinBox()
for label, widget in self.inputs.items():
self.layout().addRow(label, widget)
self.line_items = QTableWidget(rowCount=10, columnCount=3)
self.line_items.setHorizontalHeaderLabels(['Job', 'Rate', 'Hours'])
self.line_items.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.layout().addRow(self.line_items)
for row in range(self.line_items.rowCount()):
for col in range(self.line_items.columnCount()):
if col > 0:
w = QSpinBox()
self.line_items.setCellWidget(row, col, w)
submit = QPushButton('Create Invoice', clicked=self.on_submit)
# + vvvvvv vvvvvvvvvvvvv
_print = QPushButton('Print Invoice', clicked=self.window().printpreviewDialog) # + _print, + self.window()
self.layout().addRow(submit, _print) # + _print
def on_submit(self):
data = {'c_name': self.inputs['Customer Name'].text(),
'c_addr': self.inputs['Customer Address'].toPlainText(),
'i_date': self.inputs['Invoice Date'].date().toString(),
'i_due': self.inputs['Invoice Date'].date().addDays(self.inputs['Days until Due'].value()).toString(),
'i_terms': '{} days'.format(self.inputs['Days until Due'].value()),
'line_items': list()}
for row in range(self.line_items.rowCount()):
if not self.line_items.item(row, 0):
continue
job = self.line_items.item(row, 0).text()
rate = self.line_items.cellWidget(row, 1).value()
hours = self.line_items.cellWidget(row, 2).value()
total = rate * hours
row_data = [job, rate, hours, total]
if any(row_data):
data['line_items'].append(row_data)
data['total_due'] = sum(x[3] for x in data['line_items'])
self.submitted.emit(data)
# remove everything else in this function below this point
# +
return data # +++
class InvoiceView(QTextEdit):
dpi = 72
doc_width = 8.5 * dpi
doc_height = 6 * dpi
def __init__(self):
super().__init__(readOnly=True)
self.setFixedSize(QSize(self.doc_width, self.doc_height))
def build_invoice(self, data):
document = QTextDocument()
self.setDocument(document)
document.setPageSize(QSizeF(self.doc_width, self.doc_height))
document.setDefaultFont(font)
cursor = QTextCursor(document)
cursor.insertText(f"Customer Name: {data['c_name']}\n")
cursor.insertText(f"Customer Address: {data['c_addr']}\n")
cursor.insertText(f"Date: {data['i_date']}\n")
cursor.insertText(f"Total Due: {data['total_due']}\n")
# +
return document # +++
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
central = QWidget()
self.setCentralWidget(central)
layout = QHBoxLayout(central)
# + vvvv
self.invoiceForm = InvoiceForm(self) # + self
layout.addWidget(self.invoiceForm)
self.invoiceView = InvoiceView()
layout.addWidget(self.invoiceView)
# hide the widget right now...
self.invoiceView.setVisible(False)
self.invoiceForm.submitted.connect(self.showPreview)
def showPreview(self, data):
self.invoiceView.setVisible(True)
self.invoiceView.build_invoice(data)
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
def printpreviewDialog(self):
previewDialog = QPrintPreviewDialog()
previewDialog.paintRequested.connect(self.printPreview)
previewDialog.exec_()
def printPreview(self, printer):
# self.invoiceView.build_invoice.print_(printer)
data = self.invoiceForm.on_submit()
document = self.invoiceView.build_invoice(data)
document.print_(printer)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
if __name__ == '__main__':
main()
i used a class from stackoverflow that applies the drag and drop feature to table rows but it doesn't save images from being removed from cells when dragging and dropping any row i want some help figuring out how to fix the code to do this jop right !
the code of the whole project with database:
database used: https://gofile.io/d/c0srQ0
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import (QApplication, QComboBox, QDialog,
QDialogButtonBox, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout,
QLabel, QLineEdit, QMenu, QMenuBar, QPushButton, QSpinBox, QTextEdit,
QVBoxLayout, QStyledItemDelegate, QCheckBox, QTableWidgetItem, QAbstractItemView, QTableWidget)
import sqlite3
import random
import ast
#import qdarkstyle
class Main(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.cashed = []
self.setWindowTitle('Arabic Math Project')
self.setContentsMargins(20,20,20,20)
# create table:
#Options()
#self.setStyleSheet(qdarkstyle.load_stylesheet())
layout = QVBoxLayout()
self.searchBar = QtWidgets.QLineEdit()
self.searchBar.setAlignment(QtCore.Qt.AlignCenter)
self.searchBar.setPlaceholderText("search")
self.searchBar.textChanged.connect(self.startSearchThreading) #editingFinished()
#self.table = QtWidgets.QTableWidget()
self.table = TableWidgetDragRows()
conn = sqlite3.connect("math.db")
conn.text_factory = bytes
self.cur = conn.cursor()
data = self.cur.execute("select * from problems;").fetchall();conn.close()
self.dims = (lambda x: (len(x), len(x[0])))(data) #(rows number, col number)
[self.table.insertRow(i) for i in [i for i in range(self.dims[0])]]
[self.table.insertColumn(i) for i in [i for i in range(self.dims[1]+1)]]
#changing h-header names .
self.table.setHorizontalHeaderLabels(["Unique", "Image", "Test", "Year", "Lesson", "Page","Comment", "Options", "Options-advanced"])
for c in range(self.dims[1]):
for r in range(self.dims[0]):
if c!=1:self.table.setItem(r, c, QtWidgets.QTableWidgetItem(data[r][c].decode("utf-8")))
else:self.table.setCellWidget(r, c, self.getImage(data[r][c]))
for r in range(self.dims[0]):self.table.setCellWidget(r, self.dims[1], Options(ops=[data[r][self.dims[1]-1].decode("utf-8")], row=data[r]))
#table h & v auto - resizing .
header = self.table.horizontalHeader()
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setSectionResizeMode(2, QtWidgets.QHeaderView.Interactive)
header.setSectionResizeMode(7, QtWidgets.QHeaderView.Interactive)
headerv = self.table.verticalHeader()
headerv.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
#hide the options column cuz it doesn't concern user to see it.
self.table.setColumnHidden(7, True)
#layout setting up.
layout.addWidget(self.searchBar)
layout.addWidget(self.table)
self.setLayout(layout)
def getImage(self, image):
imageLabel = ScaledPixmapLabel()
pixmap = QtGui.QPixmap()
pixmap.loadFromData(image, "jpg")
imageLabel.setPixmap(pixmap)
return imageLabel
def startSearchThreading(self):
self.table.setRowCount(0)
self.update = updateTable(data= self.searchBar.text())
self.update.new_signal.connect(self.Search)
self.update.start()
def create(self):
self.createFormGroupBox()
return self.formGroupBox
#QtCore.pyqtSlot(int, int, bytes, bool, bytes)
def Search(self, r, c, d, newRowOrder, ops):
if newRowOrder:self.table.insertRow(self.table.rowCount()) # create new row.
if c!=1:self.table.setItem(r, c, QtWidgets.QTableWidgetItem(d.decode("utf-8")))
else:self.table.setCellWidget(r, c, self.getImage(d))
self.cashed.append(d)
if c ==self.dims[1]-1:
self.table.setCellWidget(r, self.dims[1], Options(ops=[ops.decode("utf-8")], row=self.cashed))
self.cashed = []
class updateTable(QtCore.QThread):
def __init__(self, parent=None, data=True):
super(QtCore.QThread, self).__init__()
self.data = data
new_signal = QtCore.pyqtSignal(int, int, bytes, bool, bytes)
def run(self):
currRow = 0
conn = sqlite3.connect("math.db")
conn.text_factory = bytes
self.cur = conn.cursor()
searched = self.cur.execute(" SELECT * FROM problems WHERE text LIKE ('%' || ? || '%') ", (self.data,)).fetchall()
dims = (lambda x: (len(x), len(x[0])))(searched) if searched!=[] else (0, 0)
for r in range(dims[0]):
for c in range(dims[1]):
self.new_signal.emit(r, c, searched[r][c], True if c==0 else False, searched[r][dims[1]-1])
class ScaledPixmapLabel(QtWidgets.QLabel):
def __init__(self):
super().__init__()
self.setScaledContents(True)
def paintEvent(self, event):
if self.pixmap():
pm = self.pixmap()
originalRatio = pm.width() / pm.height()
currentRatio = self.width() / self.height()
if originalRatio != currentRatio:
qp = QtGui.QPainter(self)
pm = self.pixmap().scaled(self.size(), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
rect = QtCore.QRect(0, 0, pm.width(), pm.height())
rect.moveCenter(self.rect().center())
qp.drawPixmap(rect, pm)
return
super().paintEvent(event)
class Options(QtWidgets.QWidget):
def __init__(self, parent=None,row=[], ops=[]):
super(Options, self).__init__(parent)
self.i = random.randint(1, 100)
self.row = [j.decode("utf-8") if i!=1 else "image" for i,j in enumerate(row)]
self.formLayout = QFormLayout()
self.setStyleSheet(open("styles.css").read())
ops = ast.literal_eval(ops[0]) if ops!=[] else {}
for i,j in ops.items():
widget = {"lineedit": QLineEdit(), "combobox": QComboBox(), "spinbox": QSpinBox(), "checkbox": QCheckBox()}
title = j.get("title", "")
combos = j.get("combos", [])
if i == "lineedit":widget[i].setText(j.get("default", ""))
elif i == "combobox":widget[i].addItems(combos)
elif i == "checkbox":
widget[i].setCheckState((lambda x: {1:2, 2:2, 0:0}[x])(j.get("default", 0)))
widget[i].setText(j.get("text", ""))
elif i == "spinbox":
widget[i].setMaximum(j.get("max", 100))
widget[i].setMinimum(j.get("min", 0))
self.formLayout.addRow(QLabel(title), widget[i])
self.btn = QtWidgets.QPushButton("Submit")
self.btn.clicked.connect(self.do)
self.formLayout.addWidget(self.btn)
self.formLayout.setFormAlignment(QtCore.Qt.AlignVCenter)
self.setLayout(self.formLayout)
def do(self):
#[STOPED HERE]
heads, tails = [], []
for i in range(self.formLayout.count()):
w = self.formLayout.itemAt(i).widget()
if i%2==0:heads.append(w.text())
elif isinstance(w, QLineEdit):tails.append(w.text())
elif isinstance(w, QComboBox):tails.append(w.currentText())
elif isinstance(w, QSpinBox):tails.append(w.value())
elif isinstance(w, QCheckBox):tails.append(w.checkState())
values = {i:j for i,j in zip(heads, tails)}
print(self.row[:-1]+[values])
class TableWidgetDragRows(QTableWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setDragEnabled(True)
self.setAcceptDrops(True)
self.viewport().setAcceptDrops(True)
self.setDragDropOverwriteMode(False)
self.setDropIndicatorShown(True)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
#self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setDragDropMode(QAbstractItemView.InternalMove)
def dropEvent(self, event: QDropEvent):
if not event.isAccepted() and event.source() == self:
drop_row = self.drop_on(event)
rows = sorted(set(item.row() for item in self.selectedItems()))
print(rows)
rows_to_move = [[QTableWidgetItem(self.item(row_index, column_index)) for column_index in range(self.columnCount())]
for row_index in rows]
for row_index in reversed(rows):
self.removeRow(row_index)
if row_index < drop_row:
drop_row -= 1
for row_index, data in enumerate(rows_to_move):
row_index += drop_row
self.insertRow(row_index)
for column_index, column_data in enumerate(data):
self.setItem(row_index, column_index, column_data)
event.accept()
for row_index in range(len(rows_to_move)):
self.item(drop_row + row_index, 0).setSelected(True)
self.item(drop_row + row_index, 1).setSelected(True)
super().dropEvent(event)
def drop_on(self, event):
index = self.indexAt(event.pos())
if not index.isValid():
return self.rowCount()
return index.row() + 1 if self.is_below(event.pos(), index) else index.row()
def is_below(self, pos, index):
rect = self.visualRect(index)
margin = 2
if pos.y() - rect.top() < margin:
return False
elif rect.bottom() - pos.y() < margin:
return True
# noinspection PyTypeChecker
return rect.contains(pos, True) and not (int(self.model().flags(index)) & Qt.ItemIsDropEnabled) and pos.y() >= rect.center().y()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
main.resize(600,600)
main.show()
app.exec_()
You are setting images using QLabels set as cell widgets. Setting cell widgets is only a feature used by the table UI: it is completely unrelated to the underlying model and the QTableWidgetItems know nothing about them also. So, it's completely up to you to check if a cell has a widget set and move that widget accordingly.
Semi pseudo-code:
def dropEvent(self, event: QDropEvent):
if not event.isAccepted() and event.source() == self:
drop_row = self.drop_on(event)
rows = sorted(set(item.row() for item in self.selectedItems()))
print(rows)
rows_to_move = []
images = []
for row_index in rows:
items = []
for column_index in range(self.columnCount()):
items.append(QTableWidgetItem(
self.item(row_index, column_index)))
widget = self.cellWidget(row_index, column_index)
if isinstance(widget, ScaledPixmapLabel):
images.append((row_index, column_index, widget.pixmap()))
rows_to_move.append(items)
# ...
for row, column, pixmap in widgets:
self.setCellWidget(row + drop_row, column,
ScaledPixmapLabel(pixmap))
As you can see I recreated the label instance each time, that's because when you set a cell widget, the table takes its ownership. While you could reparent the widget, the result might be undefined, so it's better to create a brand new instance based on the pixmap.
Tip: avoid using inline if/else or for loops: there's absolutely no benefit in doing it, and the only result is that it makes your code much less readable
In my chart, I need to store many info in every QPointF, but when I subclass QPointF, it seems that it has no effect, can somebody give me good solution or idea?
The code is:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtPrintSupport import *
from PyQt5.QtChart import *
import random
class MyPoint(QPointF):
def __init__(self, *args):
super().__init__(*args)
def set_name(self, name):
self.name = name
def set_info(self, info):
self.info = info
class DemoChar(QChartView):
def __init__(self):
super().__init__()
self.setRenderHint(QPainter.Antialiasing)
self.chart = QChart()
self.chart.setTitle('Demo')
self.chart.setAnimationOptions(QChart.SeriesAnimations)
self.setChart(self.chart)
self.lineItem = QGraphicsLineItem(self.chart)
series = QLineSeries(name="random serie")
series.setPointsVisible(True)
series.clicked.connect(self.on_click)
self.series = series
for i in range(20):
#series << QPointF(0.1 * i, random.uniform(-10, 10))
pt = MyPoint( 0.1 * i, random.uniform(-10, 10) )
pt.set_name(str(pt))
pt.set_info(str(random.randint(1, 100)))
series << pt
self.chart.addSeries(series)
self.chart.createDefaultAxes()
def on_click(self, pt):
one_pt = self.series.pointsVector()[0]
print(one_pt)
# print(one_pt.name) #the point have no name, info attribute
app = QApplication([])
demo = DemoChar()
demo.show()
app.exec()
When you pass a QPointF to a QLineSeries, the copy constructor is used that only copies the x and y values, that is, it does not copy the object but only some attributes.
So instead of implementing a custom QPointF it is better to implement a model that is mapped to a QLineSeries using QVXYModelMapper:
import random
from PyQt5.QtCore import pyqtSlot, QPointF, Qt
from PyQt5.QtGui import QPainter, QStandardItem, QStandardItemModel
from PyQt5.QtWidgets import QApplication
from PyQt5.QtChart import QChart, QChartView, QLineSeries, QVXYModelMapper
class CustomModel(QStandardItemModel):
def add_point(self, pt, name="", info=""):
items = []
for value in (pt.x(), pt.y(), name, info):
it = QStandardItem()
it.setData(value, Qt.DisplayRole)
items.append(it)
self.appendRow(items)
def get_data(self, row):
if 0 <= row < self.rowCount():
pt = QPointF(
self.item(row, 0).data(Qt.DisplayRole),
self.item(row, 1).data(Qt.DisplayRole),
)
name = self.item(row, 2).data(Qt.DisplayRole)
info = self.item(row, 3).data(Qt.DisplayRole)
return pt, name, info
class DemoChar(QChartView):
def __init__(self, parent=None):
super().__init__(parent)
self.setRenderHint(QPainter.Antialiasing)
self.chart = QChart()
self.chart.setTitle("Demo")
self.chart.setAnimationOptions(QChart.SeriesAnimations)
self.setChart(self.chart)
self.model = CustomModel()
self.series = QLineSeries(name="random serie")
self.series.setPointsVisible(True)
self.series.clicked.connect(self.on_click)
self.mapper = QVXYModelMapper(xColumn=0, yColumn=1)
self.mapper.setModel(self.model)
self.mapper.setSeries(self.series)
for i in range(20):
pt = QPointF(0.1 * i, random.uniform(-10, 10))
name = "name-{}".format(i)
info = str(random.randint(1, 100))
self.model.add_point(pt, name, info)
self.chart.addSeries(self.series)
self.chart.createDefaultAxes()
#pyqtSlot(QPointF)
def on_click(self, pt):
# first point
index = 0
value = self.model.get_data(index)
if value is not None:
pt, name, info = value
print(pt, name, info)
def main(args):
app = QApplication(args)
demo = DemoChar()
demo.show()
ret = app.exec()
if __name__ == "__main__":
import sys
sys.exit(main(sys.argv))
I have made a custom QGraphicsView that I'm adding as a widget into an app form I made using Qt Designer. Everything appears to be working including the mouse clicks. The method that does the drawing gets called but I'm having trouble getting to actually repaint on the screen. I tried creating a copy of the paintEvent method and calling in the mouseEvent method. I also tried calling the paintEvent directly. I Can't seem to get it to repaint with the mouseEvent. Here is the code:
import sys
from PySide2 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QPushButton, QLineEdit, QAction, QSlider
from PySide2.QtWidgets import QListWidget, QTabWidget, QGraphicsView, QGraphicsScene
from PySide2.QtWidgets import QSpinBox, QWidget, QDialog, QVBoxLayout
from PySide2.QtGui import QPixmap, QImage, QMatrix, QPainter, QColor
from PySide2.QtGui import QMouseEvent, QCursor, QPaintEvent
from PySide2.QtCore import QFile, QObject, SIGNAL
import cv2
import numpy as np
import math
class Display_Pixels(QGraphicsView):
def __init__(self, parent=None):
QGraphicsView.__init__(self, parent=parent)
#super().__init__()
self.initUI()
self.img = cv2.imread('roi.jpg')
def initUI(self):
#self.setGeometry(100, 100, 450, 450)
#self.setWindowTitle('By Pixel')
#self.setMouseTracking(True)
#self.show()
res = 40
self.grid = np.array([ [-1] * res for n in range(res)]) # list comprehension
#print(self.grid.shape)
def paintEvent(self, e):
qp = QPainter()
qp.begin(self.viewport())
self.drawRectangles(qp)
qp.end()
def mousePaintEvent(self):
qp = QPainter()
qp.begin(self.viewport())
self.drawRectangles(qp)
qp.end()
def drawRectangles(self, qp, w = 20):
print("Drawing")
mode = 0
x,y = 0,0 # starting position
lr = 20
hr = 35
col = QColor(0, 0, 0)
col.setNamedColor('#d4d4d4')
qp.setPen(col)
#print(self.img.shape)
for g_row, img_row in zip(self.grid, self.img):
#print(img_row.shape)
for g_col, img_col in zip(g_row, img_row):
r, g, b = (img_col[0], img_col[1], img_col[2])
#print(r,g,b)
if g_col == 1:
if mode == 0:
r = int(math.log(r+1)*lr)
g = int(math.log(g+1)*hr)
b = int(math.log(b+1)*lr)
elif mode == 1:
if r+50 <= 220: r = r+50
if g+80 <= 255: g = g+80
if b+50 <= 220: b = b+50
else:
if r+70 <= 220: r = r+70
if g+140 <= 255: g = g+140
if b+70 <= 220: b = b+70
qp.setBrush(QColor(r, g, b))
qp.drawRect(x, y, w, w)
else:
qp.setBrush(QColor(r, g, b))
qp.drawRect(x, y, w, w)
#qp.setBrush(QColor(200, 0, 0))
#qp.drawRect(x, y, w, w)
x = x + w # move right
y = y + w # move down
x = 0 # rest to left edge
def mousePressEvent(self, QMouseEvent):
w = 16.0
#print("MOUSE:")
#print('(', int(QMouseEvent.x()/w), ', ', int(QMouseEvent.y()/w), ')')
#print (QMouseEvent.pos())
x = float(QMouseEvent.x())
y = float(QMouseEvent.y())
self.grid[int(y/w)][int(x/w)] = -1 * self.grid[int(y/w)][int(x/w)]
#print(img[int(y/w), int(x/w), :])
self.paintEvent(QPaintEvent)
#self.mousePaintEvent()
self.update()
self.repaint()
if __name__ == '__main__':
app = QApplication.instance()
if app is None:
app = QApplication(sys.argv)
px = Display_Pixels()
px.show()
sys.exit(app.exec_())
You should not call the paintEvent method directly, creating a method with a similar name will not magically be called. You have to call the update() method of the viewport().
def mousePressEvent(self, event):
w = 16.0
x = int(event.x()*1.0/w)
y = int(event.y()*1.0/w)
s1, s2 = self.grid.shape
# verify
if 0 <= y < s1 and 0 <= x < s2:
self.grid[x][y] = -self.grid[x][y]
self.viewport().update()