Qtableview Printing

How to print a QTableView

One way to do it is to dump the table contents into a QTextDocument, and then print that.

The following demo uses a simple text-table, but html could be used to get more sophisticated formatting:

from PyQt4 import QtGui, QtCore

class Window(QtGui.QWidget):
def __init__(self, rows, columns):
QtGui.QWidget.__init__(self)
self.table = QtGui.QTableView(self)
model = QtGui.QStandardItemModel(rows, columns, self.table)
for row in range(rows):
for column in range(columns):
item = QtGui.QStandardItem('(%d, %d)' % (row, column))
item.setTextAlignment(QtCore.Qt.AlignCenter)
model.setItem(row, column, item)
self.table.setModel(model)
self.buttonPrint = QtGui.QPushButton('Print', self)
self.buttonPrint.clicked.connect(self.handlePrint)
self.buttonPreview = QtGui.QPushButton('Preview', self)
self.buttonPreview.clicked.connect(self.handlePreview)
layout = QtGui.QGridLayout(self)
layout.addWidget(self.table, 0, 0, 1, 2)
layout.addWidget(self.buttonPrint, 1, 0)
layout.addWidget(self.buttonPreview, 1, 1)

def handlePrint(self):
dialog = QtGui.QPrintDialog()
if dialog.exec_() == QtGui.QDialog.Accepted:
self.handlePaintRequest(dialog.printer())

def handlePreview(self):
dialog = QtGui.QPrintPreviewDialog()
dialog.paintRequested.connect(self.handlePaintRequest)
dialog.exec_()

def handlePaintRequest(self, printer):
document = QtGui.QTextDocument()
cursor = QtGui.QTextCursor(document)
model = self.table.model()
table = cursor.insertTable(
model.rowCount(), model.columnCount())
for row in range(table.rows()):
for column in range(table.columns()):
cursor.insertText(model.item(row, column).text())
cursor.movePosition(QtGui.QTextCursor.NextCell)
document.print_(printer)

if __name__ == '__main__':

import sys
app = QtGui.QApplication(sys.argv)
window = Window(25, 2)
window.resize(300, 400)
window.show()
sys.exit(app.exec_())

Print all visible rows in QTableView in c++

You can get the current line number through the value() of the verticalScrollbar, and you can also get the number of displayable lines through pagestep().

This is my code ,you can try it:

void TesWidget::onbtnClicked()
{
int start_index = ui.tableView->verticalScrollBar()->value();
int page_cnt = ui.tableView->verticalScrollBar()->pageStep();
int end_index = start_index + page_cnt;

int row_cnt = model_->rowCount();
int col_cnt = model_->columnCount();

QString text;
for (int i = start_index; i < row_cnt && i <= end_index; i++)
{
for (int j = 0; j < col_cnt; j++)
{
text.append(QStringLiteral("%1 ").arg(model_->item(i,j)->text()));
}
text.append("\n");
}
qDebug() << text;
}

Printing QTableView using render method

Ok, here is my solution. Would be nice to hear your opinion.

PrintTableModel* pTableModel = new PrintTableModel();

QTableView* pTableView = new QTableView;
pTableView->setModel(pTableModel);

int width = 0;
int height = 0;
int columns = pTableModel->columnCount();
int rows = pTableModel->rowCount();

pTableView->resizeColumnsToContents();

for( int i = 0; i < columns; ++i ) {
width += pTableView->columnWidth(i);
}

for( int i = 0; i < rows; ++i ) {
height += pTableView->rowHeight(i);
}

pTableView->setFixedSize(width, height);
pTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
pTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

This code helped me. To print the table correctly, you just can perform a render call:

pTableView->render(printer);

Printing QTableView with vertical and horizontal headers

If you want to print the headers you must add them as shown in the following code:

void Snapshot_finance::on_pushButton_print_clicked()
const QString format("<td>%1</td>");
QString html;
QAbstractItemModel *md = ui->tableView->model();
html = "<html><body><table border=\"0\">";

html += "<td></td>";
for(int column = 0; column < md->columnCount();
column++) {
QString data = md->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString();
html += format.arg(data);
}
for(int row = 0; row < md->rowCount() ; row++) {
html += "<tr>";
QString data = md->headerData(row, Qt::Vertical, Qt::DisplayRole).toString();
html += format.arg(data);
for(int column = 0; column < md->columnCount();
column++) {
QString data = md->index(row, column).data(Qt::DisplayRole).toString();
html += format.arg(data);
}
html += "</tr>";
}
html += "</table></body></html>";

QPrinter printer;
QPrintDialog *dialog = new QPrintDialog(&printer);
if(dialog->exec() == QDialog::Accepted) {
QTextDocument document;
document.setHtml(html);
document.print(&printer);
}
}

TableView:

Sample Image

Part of PDF

Sample Image

The code that is implemented for the test can be found in the following link.

Reduce excessive painting when populating QTableView in batches

Qt models provide a couple of useful functions:

  • canFetchMore(parent), which says if the model can load more data (for a given parent);
  • fetchMore(parent) tells the model to do load more data (but the model decides the amount of the "more");

Those functions are called by item views so that when they can request the model if there's more data to load whenever the user has scrolled to the end (usually, at the bottom) and eventually tell the model to do the fetching.

Considering the above, what you need to do is to implement a model that starts with a specified minimum amount of data, provides both fetching methods to load further data, and then add a timer to the view to request further fetching whenever it's possible, unless the current state() is EditingState or the model cannot fetch more data.

Since your code is too complex for an answer, I created a simpler example to explain the concept; the second columns shows when the index has been fetched starting from the moment the model has been created:

from PyQt5 import QtCore, QtWidgets

class TestModel(QtCore.QAbstractTableModel):
totalRowCount = 1980
currentRowCount = 25
fetchAmount = 25
def __init__(self):
super().__init__()
self.eTimer = QtCore.QElapsedTimer()
self.eTimer.start()
self.times = {
r:0 for r in range(self.currentRowCount)
}

def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return ('Item', 'Loading time')[section]
return super().headerData(section, orientation, role)

def rowCount(self, parent=None):
return self.currentRowCount

def columnCount(self, parent=None):
return 2

def canFetchMore(self, parent=None):
return self.currentRowCount < self.totalRowCount

def fetchMore(self, parent=None):
maxRow = min(self.totalRowCount, self.currentRowCount + self.fetchAmount)
self.beginInsertRows(QtCore.QModelIndex(), self.currentRowCount, maxRow - 1)
t = self.eTimer.elapsed() * .001
self.times.update({r:t for r in range(self.currentRowCount, maxRow)})
self.currentRowCount += self.fetchAmount
self.endInsertRows()

def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
if index.column() == 0:
return 'Item at row {}'.format(index.row() + 1)
return self.times[index.row()]

def flags(self, index):
return super().flags(index) | QtCore.Qt.ItemIsEditable

class TestTable(QtWidgets.QTableView):
def __init__(self):
super().__init__()
self.resize(640, 480)
self._model = TestModel()
self.setModel(self._model)
self.horizontalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch)
self.fetchTimer = QtCore.QTimer(
interval=1000, singleShot=True, timeout=self.fetchMore)
self.fetchTimer.start()

def fetchMore(self):
if self.model().canFetchMore():
if self.state() != self.EditingState:
self.model().fetchMore()
self.fetchTimer.start()


if __name__ == '__main__':
app = QtWidgets.QApplication([])
w = TestTable()
w.show()
app.exec_()

Note: the parent argument for canFetchMore and fetchMore is mandatory, just like rowCount and columnCount.

Obviously, if your model requires some time to fetch the actual data and finally insert new indexes (for instance, a remote database or network requests), you need to implement that with a further delay timer to "queue" the fetch requests.

You can create another single-shot timer in the model, and then push the fetching (beginInsertRows and endInsertRows) whenever the model is able to do that, even using a separate thread.

As a further and unrelated suggestion, please try to put more efforts in making your examples more minimal: your question is about general updating of a view with multiple items, all delegate aspects and resizing of items are completely unnecessary for that, and they just become an annoying and unnecessary distraction from what we should be focusing into.

display data from MySQL database to qtableview python pyqt5

If you want to display the data automatically, do not use method because if you want you should call it. The code will be messy but it will work for you.

Months ago I had create a CRUD app using tablewidget, but if you want to display just the data, you should use tableview.

This code will work for you if you want use qtablewidget, but if you use tableview you should create model or use pyqt predefined classes

try:

connection = mc.connect(host=cr.host, user=cr.user, password=cr.password, database=cr.database)

cur = connection.cursor()
cur.execute("SELECT * FROM student")

result = cur.fetchall()
self.list.setRowCount(0)
print("ff")
for row_number, row_data in enumerate(result):
self.list.insertRow(row_number)

for column_number, data in enumerate(row_data):
self.list.setItem(row_number, zcolumn_number, QTableWidgetItem(str(data)))

except mc.Error as e:
print(e)

Note: list is the name of the table



Related Topics



Leave a reply



Submit