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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python

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

Q&A

解決済

1回答

2811閲覧

[PyQt-pyhton]同じwindow上でレイヤーを分けて表示したい

Kenza

総合スコア21

Python

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

1グッド

0クリップ

投稿2020/05/22 08:42

編集2020/05/22 08:43

PyQt5-pythonを用いて、簡単なペイントツールを作成しています。

現状では描画領域(canvas)上にボタンが配置されているのですが、描画領域とボタンを分けて表示したいです。
イメージでいうと下図のようなレイアウトになるのが理想です。同じwindow内に描画領域とツール領域を分別して表示するにはどうすればよいでしょうか?よろしくお願いいたします。

環境:windows10

イメージ説明

python

1import sys 2from PyQt5.QtWidgets import ( 3 QWidget, QApplication, QMainWindow, QAction, 4 QFileDialog, QColorDialog, QInputDialog, QPushButton,QHBoxLayout, QVBoxLayout, QApplication) 5from PyQt5.QtGui import QPainter, QImage, QPen, qRgb 6from PyQt5.QtCore import Qt, QPoint, QRect, QSize, QDir 7from collections import deque 8from PyQt5.QtCore import pyqtSlot 9 10class MainWindow(QMainWindow): 11 def __init__(self): 12 super(MainWindow, self).__init__() 13 # Canvasクラスを呼び出すよ。 14 self.canvas = Canvas() 15 # 呼び出したら箱に入れてあげようね。そうしないと動いてくれないよ。 16 self.setCentralWidget(self.canvas) 17 18 self.initUI() 19 20 def initUI(self): 21 menubar = self.menuBar() 22 23 openAct = QAction('&Open', self) 24 openAct.setShortcut('Ctrl+O') 25 openAct.triggered.connect(self.openFile) 26 27 exitAct = QAction('&Exit', self) 28 exitAct.setShortcut('Ctrl+Q') 29 exitAct.triggered.connect(self.close) 30 31 saveAct = QAction('&Save', self) 32 saveAct.setShortcut('Ctrl+S') 33 saveAct.triggered.connect(self.saveFile) 34 35 resetAct = QAction('&Reset', self) 36 resetAct.triggered.connect(self.canvas.resetImage) 37 38 39 fileMenu = menubar.addMenu('&File') 40 fileMenu.addAction(resetAct) 41 fileMenu.addAction(openAct) 42 fileMenu.addAction(saveAct) 43 fileMenu.addAction(exitAct) 44 45 selectColorAct = QAction('&Pen Color', self) 46 selectColorAct.triggered.connect(self.selectColor) 47 48 selectWidthAct = QAction('&Pen Width', self) 49 selectWidthAct.triggered.connect(self.selectWidth) 50 51 #backAct = QAction('&Back', self) 52 #backAct.setShortcut('Ctrl+Z') 53 #backAct.triggered.connect(self.canvas.backImage) 54 55 #nextAct = QAction('&Next', self) 56 #nextAct.setShortcut('Ctrl+Y') 57 #nextAct.triggered.connect(self.canvas.nextImage) 58 59 penColorMenu = menubar.addMenu('&Pen Color') 60 penColorMenu.addAction(selectColorAct) 61 penWidthMenu = menubar.addMenu('&Pen Width') 62 penWidthMenu.addAction(selectWidthAct) 63 backMenu = menubar.addMenu('&Back') 64 #backMenu.addAction(backAct) 65 #nextMenu = menubar.addMenu('&Next') 66 #nextMenu.addAction(nextAct) 67 68 #back buttonの定義 69 button_back=QPushButton('Back',self) 70 button_back.clicked.connect(self.on_back) 71 72 73 #rest buttonの定義 74 button_reset=QPushButton('Reset',self) 75 button_reset.clicked.connect(self.on_reset) 76 77 78 hbox=QHBoxLayout() 79 hbox.addStretch(1) 80 hbox.addWidget(button_back) 81 hbox.addWidget(button_reset) 82 # 垂直なボックスを作成 83 vbox = QVBoxLayout() 84 # 垂直方向に伸縮可能なスペースを作る 85 vbox.addStretch(1) 86 # 右下にボタンが移る 87 vbox.addLayout(hbox) 88 # 画面に上で設定したレイアウトを加える 89 self.setLayout(vbox) 90 91 92 self.setGeometry(300, 300, 1000, 500) 93 self.setWindowTitle("MainWindow") 94 self.show() 95 96 @pyqtSlot() 97 def on_back(self): 98 print('PyQt5 button click') 99 self.canvas.backImage() 100 101 @pyqtSlot() 102 def on_reset(self): 103 self.canvas.resetImage() 104 105 def selectColor(self): 106 newColor = QColorDialog.getColor(self.canvas.penColor()) 107 self.canvas.setPenColor(newColor) 108 109 def selectWidth(self): 110 newWidth, ok = QInputDialog.getInt( 111 self, "select", 112 "select pen width: ", self.canvas.penWidth(), 1, 100, 1 113 ) 114 if ok: 115 self.canvas.setPenWidth(newWidth) 116 117 def openFile(self): 118 fileName, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.currentPath()) 119 if fileName: 120 self.canvas.openImage(fileName) 121 122 def saveFile(self): 123 path = QDir.currentPath() 124 print(path) 125 fileName, _ = QFileDialog.getSaveFileName(self, "Save as",path) 126 fileName=fileName+'.png' 127 if fileName: 128 print(fileName) 129 return self.canvas.saveImage(fileName) 130 else: 131 print("you couldnt save the file") 132 return False 133 134class Canvas(QWidget): 135 def __init__(self, parent = None): 136 super(Canvas, self).__init__(parent) 137 138 self.myPenWidth = 2 139 self.myPenColor = Qt.black 140 self.image = QImage() 141 self.check = False 142 self.back = deque(maxlen = 10) 143 #self.next = deque(maxlen = 10) 144 # initUIはもう必要ないから消しておこうね 145 146 147 def mousePressEvent(self, event): 148 if event.button() == Qt.LeftButton: 149 self.back.append(self.resizeImage(self.image, self.image.size())) 150 self.lastPos = event.pos() 151 self.check = True 152 153 def mouseMoveEvent(self, event): 154 if event.buttons() and Qt.LeftButton and self.check: 155 self.drawLine(event.pos()) 156 157 def mouseReleaseEvent(self, event): 158 if event.button() == Qt.LeftButton and self.check: 159 self.drawLine(event.pos()) 160 self.check = False 161 162 def drawLine(self, endPos): 163 painter = QPainter(self.image) 164 painter.setPen( 165 QPen(self.myPenColor, self.myPenWidth, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) 166 ) 167 painter.drawLine(self.lastPos, endPos) 168 self.update() 169 self.lastPos = QPoint(endPos) 170 171 def paintEvent(self, event): 172 painter = QPainter(self) 173 rect = event.rect() 174 painter.drawImage(rect, self.image, rect) 175 176 def resizeEvent(self, event): 177 if self.image.width() < self.width() or self.image.height() < self.height(): 178 changeWidth = max(self.width(), self.image.width()) 179 changeHeight = max(self.height(), self.image.height()) 180 self.image = self.resizeImage(self.image, QSize(changeWidth, changeHeight)) 181 self.update() 182 183 def resizeImage(self, image, newSize): 184 changeImage = QImage(newSize, QImage.Format_RGB32) 185 changeImage.fill(qRgb(255, 255, 255)) 186 painter = QPainter(changeImage) 187 painter.drawImage(QPoint(0, 0), image) 188 return changeImage 189 190 def saveImage(self, filename): 191 if self.image.save(filename): 192 return True 193 else: 194 return False 195 196 def openImage(self, filename): 197 image = QImage() 198 if not image.load(filename): 199 return False 200 201 self.image = image 202 self.update() 203 return True 204 205 def penColor(self): 206 return self.myPenColor 207 208 def penWidth(self): 209 return self.myPenWidth 210 211 def setPenColor(self, newColor): 212 self.myPenColor = newColor 213 214 def setPenWidth(self, newWidth): 215 self.myPenWidth = newWidth 216 217 def resetImage(self): 218 self.image.fill(qRgb(255, 255, 255)) 219 self.update() 220 221 def backImage(self): 222 if self.back: 223 back_ = self.back.pop() 224 #self.next.append(back_) 225 self.image = QImage(back_) 226 self.update() 227 228 #def nextImage(self): 229 #if self.next: 230 #next_ = self.next.pop() 231 #self.back.append(next_) 232 #self.image = QImage(next_) 233 #self.update() 234 235if __name__ == '__main__': 236 # 動け~ 237 app = QApplication(sys.argv) 238 # ここがCanvasのままだと何も表示されないよ。気を付けようね 239 ex = MainWindow() 240 sys.exit(app.exec_())
teamikl👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

問題の原因: レイアウトの競合

  • setCentralWidget に Canvas を設定
  • setLayout でボタン類を設置

という二通りの方法でウィジェットを配置している為、このような警告メッセージが出てると思います。

QWidget::stLayout: Attempting to set QLayout "" on QMainWIndow "",

which already has a layout


解決策:

setCentralWidget(canvas)を辞めて、
ボタン類とキャンバスの親となるウィジェットを作り、
キャンバスとボタン類を共通のレイアウトに配置するようにします。

※ コードは概要のみ

MainWindow - QWidget <-- 共通の親を作る - Canvas - QPushButton 等

python

1# "self" は、class MainWindow 内を想定 2 3frame = QWidget() 4canvas = Canvas() 5button = QPushButton("TEST") 6 7hbox = QHBoxLayout(frame) # <- 親を指定すると、frame.setLayout(hbox) を省略できます 8hbox.addWidget(canvas) 9hbox.addWidget(button) 10 11self.setCentralWidget(frame)

他の方法: 共通の親ウィジェットを作らない場合は、setCentralWidget を使いませんが、
共通のレイアウトに Canvas と ボタン類を入れる点は同じです。

python

1canvas = Canvas() 2button = QPushButton("TEST") 3 4hbox = QHBoxLayout(self) 5hbox.addWidget(canvas) 6hbox.addWidget(button) 7 8self.setLayout(hbox)

遅れましたが一応投稿。レイアウト表示に焦点を絞るために、
キャンバスの実装やイベント類のコードは省いてます。ブラウザ内で実行確認可能です。

source on repl.it

イメージ説明

投稿2020/05/22 09:32

編集2020/05/22 13:08
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Kenza

2020/05/22 10:40

teamiklさん、返信ありがとうございます。 前者の親フレームにcanvasなどを載せてく方向で進めていきたいと思います。 その方向性に当たり書き直したのですが、ターミナルで実行したところwindow画面が起動しませんでした。 下記のurlに書きなおしたコードは置いています。 また、 MainWindow  pairen_frame  -canvas  -button という構造は順守したつもりですが、おそらく知識が追い付いていない部分があり完成には至りませんでした。 よろしくお願いいたします。 https://drive.google.com/file/d/1lih2hx5gMYd-pN_nQwlnLRcfquLSsjmW/view?usp=sharing
teamikl

2020/05/22 11:53

元のソースからの変更だと少し範囲が広くなりそうだったので、 コードは概要のみになってしまいましたが、表示のみ動くところまで書いてみますね。 後もう一点、ボタン類の親が mainwindow 直接になってますが、 ここも間に一つ親となるフレームを作った方が管理しやすくなります。 - MainWindow  - parent_frame (HBoxLayout)   - canvas   - sub_frame (VBoxLayout)    - buttonA    - buttonB 問題の焦点を絞るためには、現状のコードを改変の前に UI表示のテスト用にイベント類の処理を取り除いたコードを準備し まずは、表示のみで成功するか試してみると良いです。 そして、元のコードも残しておいて、差分を見て変更箇所をピックアップし 現状のコードに反映。
teamikl

2020/05/22 11:59

ソース見れました。ほぼ修正はできてそうな感じですね、 表示されない原因は show() が何処かに行ってしまったためです。 ex = MainWindow()の後に ex.show() を呼び出してみてください。
teamikl

2020/05/22 12:00

>show() が何処かに行って savefile() メソッドの中に一連のコードがありました。 これを適切な位置に移動すると直るはずです。
Kenza

2020/05/22 12:42

できました!!!! teamiklさんのおかげで、解決することができました! ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問