0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Qt6 でハマった件(2)

0
Posted at

はじめに

 前回、Python で PyQt6 を使っていたときにハマった話を紹介したが、

 ちょっと気になったので C++ で直に Qt6 のライブラリを使った場合を確認してみたという話。

 先に結論を書いておくが、結果は同じ。(考えてみれば当たり前なんだろうけど)

  • ResizeToContents を設定している QHeaderView を使った QTableView で、アイテム数の少ないモデルに切り替えた際、Out of range を起こす。

 したがって、新たな情報はあまり無いが、メモを残しておく。

確認環境

OS: Ubuntu 22.04.2 (WSL2: Windows 11 Home (22H2))
Qt: 6.5.2 (自分でインストール)

確認用コード

 例によって動かすことを主眼に、シンプルに書いているので、かなり簡素化したコードになっている。少なくともコーディングスタイルのお手本にはけっしてならない類のモノだとは、先にお断りしておく。

main.h
// -*- coding: utf-8 -*-
#ifndef MAIN_H
#define MAIN_H

#include <vector>
#include <string>

#include <QMainWindow>
#include <QMenuBar>
#include <QTableView>
#include <QHeaderView>
#include <QAbstractTableModel>

class _Model: public QAbstractTableModel
{
  Q_OBJECT
public:
  using items_type = std::vector<std::vector<std::string> >;
  using headers_type = std::vector<std::string>;

  _Model(items_type const &items, headers_type const &headers)
    : _items(items), _headers(headers) {}
  ~_Model() override = default;

  int rowCount(const QModelIndex &parent = QModelIndex()) const {
    return _items.size(); }
  int columnCount(const QModelIndex &parent = QModelIndex()) const {
    return _headers.size(); }
  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
  QVariant headerData(int section, Qt::Orientation orientation,
		      int role = Qt::DisplayRole) const;
private:
  items_type _items;
  headers_type _headers;
};

class _HHeader: public QHeaderView
{
  Q_OBJECT
public:
  _HHeader(QWidget *parent = nullptr)
    : QHeaderView(Qt::Horizontal, parent) {
    setSectionResizeMode(ResizeMode::ResizeToContents);
  }
  ~_HHeader() override = default;
};

class _Table: public QTableView
{
  Q_OBJECT
public:
  _Table(QWidget *parent = nullptr)
    : QTableView(parent) {
    setHorizontalHeader(new _HHeader(this));
  }
  ~_Table() override = default;
};

class Main: public QMainWindow
{
  Q_OBJECT
public:
  Main();
  ~Main() override = default;
public slots:
  void setModel1();
  void setModel2a();
  void setModel2b();
private:
  void _setupUI();
  QTableView *tv;
  QAbstractTableModel *m1, *m2;
};

#endif
main.cxx
// -*- coding: utf-8 -*-
#include <iostream>
#include <QApplication>
#include "main.h"

QVariant _Model::data(const QModelIndex &index, int role) const
{
  if (role == Qt::DisplayRole) {
    auto nrow = rowCount();
    auto ncol = columnCount();
    if (index.row () >= nrow || index.column() >= ncol) {
      std::cout << "Model::data(" << index.row() << "," << index.column()
		<< "), role: " << role << std::endl;
      return QVariant();
    }
    return QString(_items[index.row()][index.column()].c_str());
  }
  return QVariant();
}

QVariant _Model::headerData(int section, Qt::Orientation orientation,
			    int role) const
{
  if (orientation == Qt::Horizontal &&
      role == Qt::DisplayRole) {
    auto ncol = columnCount();
    if (section >= ncol) {
      std::cout << "Model::section: " << section
		<< ", columnCont: " << ncol
		<< ", orientation: " << orientation
		<< ", role: " << role << std::endl;
      return QVariant();
    }
    return QString(_headers[section].c_str());
  }
  return QAbstractTableModel::headerData(section, orientation, role);
}

Main::Main()
{
  _setupUI();
  m1 = new _Model({{"a1", "b1", "c1"}, {"a2", "b2", "c2"}},
		   {"#1", "#2", "#3"});
  m2 = new _Model({}, {});
}

void Main::_setupUI()
{
  setWindowTitle("test");
  resize(600, 400);

  auto filemenu = menuBar()->addMenu("テスト(&T)");

  connect(filemenu->addAction("Model #1"), &QAction::triggered,
	  this, &Main::setModel1);
  connect(filemenu->addAction("Model #2a"), &QAction::triggered,
	  this, &Main::setModel2a);
  connect(filemenu->addAction("Model #2b"), &QAction::triggered,
	  this, &Main::setModel2b);
  {
    auto act = filemenu->addAction("閉じる(&X) ");
    act->setShortcut(tr("Ctrl+Q"));
    act->setStatusTip("ウィンドウを閉じる");
    connect(act, &QAction::triggered, this, &Main::close);
  }

  tv = new _Table(this);
  setCentralWidget(tv);
}

void Main::setModel1()
{
  std::cout << "setModel1" << std::endl;
  tv->setModel(m1);
}

void Main::setModel2a()
{
  std::cout << "setModel2a" << std::endl;
  tv->setModel(m2);
}

void Main::setModel2b()
{
  std::cout << "setModel2b" << std::endl;
  tv->setModel(nullptr);
  tv->setModel(m2);
}

int main(int argv, char *args[])
{
    QApplication app(argv, args);
    Main w;
    w.show();
    return app.exec();
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)

project(Qt6TableTest1 VERSION 0.0.1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)

find_package(Qt6 REQUIRED COMPONENTS Widgets)

add_executable(a.out main.cxx)
target_link_libraries(a.out PRIVATE Qt6::Widgets)

動作確認

1. コンパイルから起動までは以下のように実行すればできる(ハズ)。

実行例
$ cmake -S . -B build
$ cmake --build build/
$ ./build/a.out

2. ウィンドウが立ち上がったら、メニューの [テスト] から [Model #1] を選ぶ。
  ⇒縦2,横3のテーブルが表示される。

3. メニューの [テスト] から [Model #2a]を選ぶ。
  ⇒エラー発生。以下のようなメッセージが表示されるハズ。

実行例(続き)
$ ./build/a.out
setModel1
setModel2a
Model::section: 0, columnCont: 0, orientation: 1, role: 0
Model::section: 1, columnCont: 0, orientation: 1, role: 0
Model::section: 2, columnCont: 0, orientation: 1, role: 0

(あ、スペルミスがある…… orz)

終わりに

 回避策としては、いったん nullptr を setModel に入れれば良いことはわかっている。
 なので、これ以上追求するモチベーションはあまり無いのだけれど、もしかしたら Qt のコードを追ってデバッグしてみるのも一興かも?

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?