【QT tableWidget 报表打印】实现单元格合并、拉动调节单元格大小与最终PDF打印

主要功能概述:

最近实习在学QT,做了一个小练习,实现如下图所示的功能,在tableWidget实现表格格式设计后,使用QPainter和QPrinter实现表格的打印,功能如下图ui所示:

一、tableWidget控件功能实现

控件功能实现csdn上有很多人写过,这里做一下整合:

1、新增与删除行列

1.1新增行列:

用insertRow(int),和insertColumn(int)实现

    //获取到当前有多少行
    int rowCount = ui->tableWidget->rowCount();
    //插入新的行
    ui->tableWidget->insertRow(rowCount);
     //获取当前的列数
     int colCount = ui->tableWidget->columnCount();
     //插入新的列
     ui->tableWidget->insertColumn(colCount);

1.2删除行列:

    //获取当前选中的行
    int curRow = ui->tableWidget->currentRow();
    //删除当前选中的行
    ui->tableWidget->removeRow(curRow);
    //获取当前选中的列
    int curCol = ui->tableWidget->currentColumn();
    //删除选中的列
    ui->tableWidget->removeColumn(curCol);

2、合并与拆分单元格

2.1合并单元格

使用selectedRanges()来获得在tableWidget上多选单元格的数据,selectRanges会返回选中区域中所包含的矩形局域数组,数组中包含每个矩形区域左上顶点和右下顶点所在的行列。

值得注意的是,如果选中区域内包含了已经被合并的单元格,那每个单元格会被当成单独的矩形区域计算(原因应该是合并是一种隐藏,selectedRanges()操作的是逻辑单元格而不是可视单元格),如果要得到大的矩形区域左上顶点和右下顶点所在的行列,需要遍历Qlist获得最大最小值。具体代码如下:

   QModelIndexList list = ui->tableWidget->selectionModel()->selectedIndexes();
   if (list.size() < 2)
   {
       QMessageBox::warning(this, "单元格合并", "所选中单元格中为单个单元格,无法合并", "确定");
       return;
   }

   int topRow = 0;
   int leftCol = 0;
   int bottomRow = 0;
   int rightCol = 0;

   QList<QTableWidgetSelectionRange> selectRanges = ui->tableWidget->selectedRanges();

      if (selectRanges.size() > 0)
      {
          topRow = selectRanges[0].topRow();
          leftCol = selectRanges[0].leftColumn();
          bottomRow = selectRanges[0].bottomRow();
          rightCol = selectRanges[0].rightColumn();
      }
      for(auto range:selectRanges)
      {
         if(range.topRow()<topRow)
             topRow=range.topRow();
         if(range.leftColumn()<leftCol)
             leftCol=range.leftColumn();
         if(range.bottomRow()> bottomRow)
             bottomRow=range.bottomRow();
         if(range.rightColumn()>rightCol)
             rightCol=range.rightColumn();
      }

    int rowSpan = (bottomRow - topRow) + 1;
    int colSpan = (rightCol - leftCol) + 1;
    ui->tableWidget->setSpan(topRow, leftCol, rowSpan, colSpan);

得到要合并的区域后使用setSpan()合并,setSpan函数原型 void setSpan(int row, int column, int rowSpan, int columnSpan);其中row表示行,column表示列,rowSpan 为行跨度,columnSpan 为列跨度。

2.2拆分单元格

setSpan跨度设为1可实现拆分单元格,使用selectedItems()可以直接获得合并区域中第一个单元格所在的行列。

     int row,col;
     QList<QTableWidgetSelectionRange> selectRanges = ui->tableWidget->selectedRanges();
     if (selectRanges.size() < 2)
     {
         QMessageBox::warning(this, QString::fromLocal8Bit("拆分表格失败"), "单元格已是最小单位,不能再进行拆分", "确定");
         return;
     }
     QList<QTableWidgetItem*> selectItems = this->ui->tableWidget->selectedItems();
     if(selectItems.size()==0)
     {
         QMessageBox::warning(this, QString::fromLocal8Bit("拆分表格失败"), "请先为表格设置元素item", "确定");
         return;
     }
     if(selectItems.size()>1)
     {
          QMessageBox::warning(this, QString::fromLocal8Bit("拆分表格失败"), "非法选择", "确定");
          return;
     }
     for(auto item:selectItems)
     {
         row= item->row();
         col=item->column();
     }
     ui->tableWidget->setSpan(row, col, 1, 1);  // 设置跨度为1

3、行列表头设置

将行列表头设为可见:

 ui->tableWidget->verticalHeader()->setVisible(true);//行头数字列是否可见
 ui->tableWidget->horizontalHeader()->setVisible(true);//列头数字列是否可见

列表头设为所有单元格可拉伸,且最后一格会自动填满tableWidget剩余区域

ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
ui->tableWidget->horizontalHeader()->setStretchLastSection(true);

4、最终效果

二、将上述格式的表格打印到PDF

1、打印设置:

打印用了QPrinter,用了调用打印预览窗打印和直接打印,在QPrinter里面可以进行一些打印设置,不如打印分辨率,纸张大小,纸张朝向等:

//调打印预览窗
void Dialog::printWithPreview()
{
    QPrinter printer(QPrinter::ScreenResolution);
    printer.setPageSize(QPrinter::A4);
    printer.setOrientation(QPrinter::Portrait);

    QPrintPreviewDialog preview(&printer);
    connect(&preview,SIGNAL(paintRequested(QPrinter*)),this,SLOT(printDocument(QPrinter*)));
    preview.setWindowState(Qt::WindowMaximized);
    preview.exec();
}

//直接打印
void Dialog::printDirect()
{
    QPrinter printer(QPrinter::ScreenResolution);
    printer.setPageSize(QPrinter::A4);
    QPrintDialog printDialog(&printer);
    printer.setOrientation(QPrinter::Portrait);

    if(printDialog.exec()==QDialog::Accepted)
    {
        printDocument(&printer);
    }
}

槽函数printDocument(QPrinter *printer)里设置画笔颜色与打印纸张页数,并调用画图函数

void Dialog::printDocument(QPrinter *printer)
{
    QPainter painter;
    painter.begin(printer);
    painter.setPen(Qt::black);
    for(int i=0;i<pageNum;i++)
    {
        drawTable(&painter);
        qDebug()<<"正在打印"<<endl;
        if(i!=pageNum-1)
        {
            printer->newPage();
            painter.setPen(Qt::black);
        }
    }
    drawTable(&painter);
    painter.end();
}

2、绘表:

在绘表函数drawTable里我们要对上面我们设置过格式的单元格进行绘制,格式设置包括合并单元格,单元格长宽比的变化等。

2.1获得打印单元格的长宽

根据控件上的单元格长宽比获得打印时每个单元格的大小,并分别用 QList<int> m_rowHeight; QList<int> m_colWidth;保存。

//此函数根据控件计算单元格长宽比
void Dialog::countRC()
{
    int m_w;
    int m_h;
    int temp_cout=0;
    int num=1;
    m_w=ui->tableWidget->horizontalHeader()->size().rwidth();
    m_h=ui->tableWidget->verticalHeader()->size().rheight();
    //qDebug()<<m_w;
    //qDebug()<<m_h;
    for(int y=0;y<m_h;y++)
    { 
       temp_cout=ui->tableWidget->rowAt(y);
       if(temp_cout==num)
       {
           m_rowHeight.append(y);
           num++;
       }
       if(temp_cout==-1)
       {
           m_rowHeight.append(y);
           break;
       }

    }
    //m_rowHeight.append(m_h);
    temp_cout=0;
    num=1;
    for(int x=0;x<m_w;x++)
    {
       temp_cout=ui->tableWidget->columnAt(x);
       if(temp_cout==num)
       {
           m_colWidth.append(x);
           num++;
       }

    }
    m_colWidth.append(m_w);

    //计算每一格在tabWidget的长宽

    for(int i=m_rowHeight.size()-1;i>0;i--)
    {
        temp_cout=m_rowHeight.at(i)-m_rowHeight.at(i-1);
        m_rowHeight[i]=temp_cout;
    }

    for(int i=m_colWidth.size()-1;i>0;i--)
    {
        temp_cout=m_colWidth.at(i)-m_colWidth.at(i-1);
        m_colWidth[i]=temp_cout;
    }

    //归一化,换算为在m_maxWeight下的宽与高
    double m_rate=double(m_maxWeight)/m_w;
    for(int i=0;i<m_rowHeight.size();i++)
    {
        m_rowHeight[i]=m_rowHeight.at(i)*m_rate;
    }
    for(int i=0;i<m_colWidth.size();i++)
    {
        m_colWidth[i]=m_colWidth.at(i)*m_rate;
    }
}

2.2绘制

使用矩形画图来绘制每个单元格,由于合并,一些单元格被隐藏,我们首先要获得需要打印的单元格,将单元格是否要打印存在Qlist<int>draw中。对于要打印的单元格,根据其跨度确定他的长宽,最终打印函数如下:

其中,m_startx,m_starty为表格起始绘制坐标,m_maxWeight是表格的最大宽。

void Dialog::drawTable(QPainter *painter)
{
    m_colNum=ui->tableWidget->columnCount();
    m_rowNum=ui->tableWidget->rowCount();
    m_colWidth.clear();
    m_rowHeight.clear();
    countRC();
    //根据当前表生成绘图数组
    QList<bool> draw;
    for (int i=0;i<m_rowNum*m_colNum;i++)
    {
        draw.append(true);
    }


    for(int i=0;i<m_rowNum;i++)
    {
        for(int j=0;j<m_colNum;j++)
        {
            if(draw[i*m_colNum+j]==false) continue;
            else
            {
                for(int ii=0;ii<ui->tableWidget->rowSpan(i,j);ii++)
                    for(int jj=0;jj<ui->tableWidget->columnSpan(i,j);jj++)
                    {
                        draw[(i+ii)*m_colNum+j+jj]=false;
                    }
                 draw[i*m_colNum+j]=true;
            }

        }

    }


    painter->setRenderHint(QPainter::Antialiasing, true);

    int startx=m_startx;
    int starty=m_starty;
    for(int i=0;i<m_rowNum;i++)
    {
        for(int j=0;j<m_colNum;j++)
        {
           if(draw[i*m_colNum+j]==true){
               //qDebug()<<"hai";
               int m_rowSpan=0;
               int m_colSpan=0;

               for(int k=0;k<ui->tableWidget->rowSpan(i,j);k++)
               {
                   m_rowSpan+=m_rowHeight.at(i+k);
               }
               for(int k=0;k<ui->tableWidget->columnSpan(i,j);k++)
               {
                   m_colSpan+=m_colWidth.at(j+k);
               }

               painter->drawRect(startx,starty,m_colSpan,m_rowSpan);
           }
           startx+=m_colWidth.at(j);
        }
        starty+=m_rowHeight.at(i);
        startx=m_startx;
    }

}

3、打印效果

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值