質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.40%

  • Python

    12808questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • Python 3.x

    10698questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Qt

    212questions

    QtはGUIプログラムの開発で広く使われているクロスプラットフォーム開発のフレームワークです。

PyQt5:QGraphicsViewとQGraphicsSceneの大きさの関係

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,055

Ykkykk

score 85

PyQtでのQGraphicsViewとQGraphicsSceneの大きさの関係がよくわかりません。

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class Example(QGraphicsItem):
    def __init__(self, width=400, height=400):
        super(Example, self).__init__()

        self.width = width
        self.height = height
        self.x_origin = width / 2
        self.y_origin = height / 2
        self.scale = 20

    def paint(self, painter, option, widget):
        painter.setPen(Qt.black)
        painter.drawLine(0, self.y_origin, self.width, self.y_origin)
        painter.drawLine(self.x_origin, 0, self.x_origin, self.height)
        print("paint")

        for i in range(100):
            x = self.x_origin + i * self.scale
            y = self.y_origin + i * self.scale
            painter.setPen(Qt.black)
            painter.setBrush(QBrush(Qt.red, Qt.SolidPattern))
            painter.drawEllipse(QPointF(x, y), 9, 9)

    def boundingRect(self):
        return QRectF(0, 0, self.width, self.height)

    def setScale(self, x):
        self.scale = x

    def getScale(self):
        return self.scale

    def repaint(self):
        self.update()

class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(0, 0, 600, 500)
        self.setWindowTitle("Ex")

        self.setWindow()

        self.show()

    def setWindow(self):
        self.w = QWidget()

        self.view = QGraphicsView()
        self.scene = QGraphicsScene(self.view)
        self.srect = self.view.rect()
        width = self.srect.width()
        height = self.srect.height()
        self.scene.setSceneRect(QRectF(self.srect))
        self.graph = Example(width, height)
        self.scene.addItem(self.graph)
        self.view.setScene(self.scene)

        label = QLabel(" X ")
        self.box = QLineEdit()
        x = self.getScale()
        self.box.setText("{}".format(x))
        update = QPushButton('Update')
        update.clicked.connect(self.setScale)

        main = QGridLayout()

        main.addWidget(self.view, 0, 0, 30, 5)
        main.addWidget(label, 0, 10, 1, 3)
        main.addWidget(self.box, 1, 10, 1, 3)
        main.addWidget(update, 2, 10, 1, 3)

        self.w.setLayout(main)
        self.setCentralWidget(self.w)

    def getScale(self):
        return self.graph.getScale()

    def setScale(self):
        self.x = int(self.box.text())
        self.graph.setScale(self.x)
        self.graph.repaint()

def main():
    app = QApplication(sys.argv)
    gui = MyWindow()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

現在、以上のような構成でウィンドウを作成していみているのですが、円が見切れる形になっております。

ここで表示されているスクロールバーは、恐らく設定されているviewの大きさ分しか動かすことができないということだと思うのですが、すべての円を表示するためにはどのようにすればよいのでしょうか?
また、右側で用意しているテキストボックスを使用して、円と円の間隔を変えるようにした場合、動的にスクロールバーの長さを変更するようなことはできるのでしょうか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

 全部表示されない原因

ここで表示されているスクロールバーは、恐らく設定されているviewの大きさ分しか動かすことができないということだと思うのですが、すべての円を表示するためにはどのようにすればよいのでしょうか?

View の大きさ以上スクロールできないのは、QGraphicsScene の SceneRect にビューの大きさを設定しているからです。

self.scene.setSceneRect(QRectF(self.srect))

これをコメントアウトして、SceneRect を設定しない場合は、QgraphicsScene はアイテムがすべて収まるサイズに QGraphicsScene が拡張されます。
今回は Example という QGraphisItem だけなので、この大きさは

def boundingRect(self):
    return QRectF(0, 0, self.width, self.height)

で定義されています。この self.width, self.height は次のコンストラクタ引数で QGraphicsView のサイズを設定していたので、QGraphicsView のサイズになります。

self.graph = Example(width, height)

 すべての円を表示するには

ここで表示されているスクロールバーは、恐らく設定されているviewの大きさ分しか動かすことができないということだと思うのですが、すべての円を表示するためにはどのようにすればよいのでしょうか?
また、右側で用意しているテキストボックスを使用して、円と円の間隔を変えるようにした場合、動的にスクロールバーの長さを変更するようなことはできるのでしょうか?

すべての円が表示されるように Example クラスの def boundingRect(self) が返す Rect を動的に変更する必要があります。

 全部表示されるように変更したコード

  • 原寸大で表示したい場合は fitInView() の2箇所をコメントアウトしてください。
  • GGraphicsItem の大きさ等変更する場合 (scale の変更など) は変更前に prepareGeometryChange() をよんでください。変更が完了したら、update() で再描画すればよいです。
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *


class Example(QGraphicsItem):
    def __init__(self, width=400, height=400):
        super(Example, self).__init__()

        self.width = width
        self.height = height
        self.x_origin = width / 2
        self.y_origin = height / 2
        self.scale = 20

    def paint(self, painter, option, widget):
        painter.setPen(Qt.black)
        painter.drawLine(0, self.y_origin, self.width, self.y_origin)
        painter.drawLine(self.x_origin, 0, self.x_origin, self.height)
        print("paint")

        for i in range(100):
            x = self.x_origin + i * self.scale
            y = self.y_origin + i * self.scale
            painter.setPen(Qt.black)
            painter.setBrush(QBrush(Qt.red, Qt.SolidPattern))
            painter.drawEllipse(QPointF(x, y), 9, 9)

    def boundingRect(self):
        width = self.y_origin + 100 * self.scale + 30
        height = self.y_origin + 100 * self.scale + 30
        return QRectF(0, 0, width, height)

    def setScale(self, x):
        self.prepareGeometryChange()
        self.scale = x
        self.update()

    def getScale(self):
        return self.scale


class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(50, 50, 600, 500)
        self.setWindowTitle("Ex")
        self.setWindow()
        self.show()

    def setWindow(self):
        self.w = QWidget()
        self.view = QGraphicsView()
        self.scene = QGraphicsScene(self.view)
        self.srect = self.view.rect()
        width = self.srect.width()
        height = self.srect.height()
        # self.scene.setSceneRect(QRectF(self.srect))
        self.graph = Example(width, height)
        self.scene.addItem(self.graph)
        self.view.setScene(self.scene)
        self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)

        label = QLabel(" X ")
        self.box = QLineEdit()
        x = self.getScale()
        self.box.setText("{}".format(x))
        update = QPushButton('Update')
        update.clicked.connect(self.setScale)

        main = QGridLayout()
        main.addWidget(self.view, 0, 0, 30, 5)
        main.addWidget(label, 0, 10, 1, 3)
        main.addWidget(self.box, 1, 10, 1, 3)
        main.addWidget(update, 2, 10, 1, 3)

        self.w.setLayout(main)
        self.setCentralWidget(self.w)

    def getScale(self):
        return self.graph.getScale()

    def setScale(self):
        self.x = int(self.box.text())
        self.graph.setScale(self.x)
        self.scene.setSceneRect(self.scene.itemsBoundingRect())
        self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)


def main():
    app = QApplication(sys.argv)
    gui = MyWindow()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

 追記 Qt の情報源

C++ も PyQt もライブラリの使い方は同じで情報量は C++ のほうが多いので、検索するときは PyQt ではなく Qt で調べたほうがいいです。(C++ の文法を知らなくても大丈夫です。)

検索は日本語で出てこなければ、英語のキーワードで調べたほうがいいです。Stack Overflow でだいたい既出です。

使用するクラスに関してだけ確認する。

  • Stack Overflow / Qt Centre などの質問サイト

例えば、「QGraphicsView の表示スケールを QGraphicsScene がすべて収まるように変更したい」と思った場合は「qgraphicsview auto scale」と検索してみると、

1個目、2個目に答えが出てきます。

Auto scale a QGraphicsView
how to Fit QGraphicsScene in a QGraphicsView

  • Qiita などの日本語のサイト

日本語の情報源もそれなりにあります。

Qiita

  • 公式の Example

Qt Creator をインストールするとかなりの数の Example がついてきます。

イメージ説明

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/09 13:49

    再度詳細にお教えいただき本当にありがとうございます。
    上記二点について確認いたしました。
    また、本筋とはそれるのですが、Qtについてどのようにご習得になられたのでしょうか?Qtのリファレンス等を読むことが中心ですか??

    キャンセル

  • 2018/10/09 14:04

    追記しました。自分は PyQt でアプリケーションを作ったりしたことはないのですが、C++ の Qt を使って GUI アプリを作成したりします。
    リファレンスは量が多いので、「使うクラスだけ読む」という活用法がおすすめです。あと検索するときは「PyQt でなく、Qt で調べる」「英語で調べる」がいいかと思います。

    キャンセル

  • 2018/10/09 15:04

    Qtで調べたほうが良いのですね。ありがとうございます。
    本筋とは異なる点までご追記いただき本当にありがとうございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.40%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Python

    12808questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • Python 3.x

    10698questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Qt

    212questions

    QtはGUIプログラムの開発で広く使われているクロスプラットフォーム開発のフレームワークです。