固定ヘッダーをqscrollareaに追加する方法は?
-
28-10-2019 - |
質問
現在、次のように定義されているQScrollareaがあります。
self.results_grid_scrollarea = QScrollArea()
self.results_grid_widget = QWidget()
self.results_grid_layout = QGridLayout()
self.results_grid_layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
self.results_grid_widget.setLayout(self.results_grid_layout)
self.results_grid_scrollarea.setWidgetResizable(True)
self.results_grid_scrollarea.setWidget(self.results_grid_widget)
self.results_grid_scrollarea.setViewportMargins(0,20,0,0)
他のレイアウト/ウィジェット内に非常に幸せにネストされ、予想どおりサイズがあります。
グリッド列に見出しを提供するために、スクロール領域の真上に配置された別のqgridlayoutを使用しています - これは機能します...しかし、特にオンデマンド(垂直)スクロールバーが表示または消える場合でも、適切にスタイルを整えた場合でも少し奇妙に見えます必要に応じて、ヘッダーはグリッド列に正しく並んでいません。それは私が知っている審美的なことです...しかし、私はちょっとうるさいです;)
他のウィジェットが追加/削除されます self.results_grid_layout
他の場所でプログラム的に。上記の最後の行は、作成されたマージンエリアを簡単に使用できると思ったので、最近追加しました。 ドキュメント setviewportmargins州の場合:
スクロールエリアの周りにマージンを設定します。これは、「ロックされた」行と列を備えたスプレッドシートなどのアプリケーションに役立ちます。限界スペースは空白のままです。 未使用のエリアにウィジェットを置きます。
しかし、私の人生のために実際にこれを達成する方法を解決することはできません。そして、私のGoogleFuが今日私を捨てたか、実際にこれを達成する方法についての情報/例はほとんどありません。
私の頭は、レイアウト(他の多くのウィジェットを含む)で制御されたウィジェットを1つだけ割り当てることができると言っています。たとえば、gridlayoutの行0にqheaderviewを追加すると、ビューポートのマージンの下に表示され、残りのレイアウトでスクロールしますか?それとも私は何かが足りなくなっていて、木のために木を見ることができませんか?
私はPython/QTを学んでいるだけなので、ヘルプ、ポインター、および/または例(できればPythonでは必須ではありません)を歓迎します!
編集:これまでに与えられたアドバイスに従って(私が思うに)、私は次の小さなテストプログラムを思いついて、物事を試してみました。
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setMinimumSize(640, 480)
self.container_widget = QWidget()
self.container_layout = QVBoxLayout()
self.container_widget.setLayout(self.container_layout)
self.setCentralWidget(self.container_widget)
self.info_label = QLabel(
"Here you can see the problem.... I hope!\n"
"Once the window is resized everything behaves itself.")
self.info_label.setWordWrap(True)
self.headings_widget = QWidget()
self.headings_layout = QGridLayout()
self.headings_widget.setLayout(self.headings_layout)
self.headings_layout.setContentsMargins(1,1,0,0)
self.heading_label1 = QLabel("Column 1")
self.heading_label1.setContentsMargins(16,0,0,0)
self.heading_label2 = QLabel("Col 2")
self.heading_label2.setAlignment(Qt.AlignCenter)
self.heading_label2.setMaximumWidth(65)
self.heading_label3 = QLabel("Column 3")
self.heading_label3.setContentsMargins(8,0,0,0)
self.headings_layout.addWidget(self.heading_label1,0,0)
self.headings_layout.addWidget(self.heading_label2,0,1)
self.headings_layout.addWidget(self.heading_label3,0,2)
self.headings_widget.setStyleSheet(
"background: green; border-bottom: 1px solid black;" )
self.grid_scrollarea = QScrollArea()
self.grid_widget = QWidget()
self.grid_layout = QGridLayout()
self.grid_layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
self.grid_widget.setLayout(self.grid_layout)
self.grid_scrollarea.setWidgetResizable(True)
self.grid_scrollarea.setWidget(self.grid_widget)
self.grid_scrollarea.setViewportMargins(0,30,0,0)
self.headings_widget.setParent(self.grid_scrollarea)
### Add some linedits to the scrollarea just to test
rows_to_add = 10
## Setting the above to a value greater than will fit in the initial
## window will cause the lineedits added below to display correctly,
## however - using the 10 above, the lineedits do not expand to fill
## the scrollarea's width until you resize the window horizontally.
## What's the best way to fix this odd initial behaviour?
for i in range(rows_to_add):
col1 = QLineEdit()
col2 = QLineEdit()
col2.setMaximumWidth(65)
col3 = QLineEdit()
row = self.grid_layout.rowCount()
self.grid_layout.addWidget(col1,row,0)
self.grid_layout.addWidget(col2,row,1)
self.grid_layout.addWidget(col3,row,2)
### Define Results group to hold the above sections
self.test_group = QGroupBox("Results")
self.test_layout = QVBoxLayout()
self.test_group.setLayout(self.test_layout)
self.test_layout.addWidget(self.info_label)
self.test_layout.addWidget(self.grid_scrollarea)
### Add everything to the main layout
self.container_layout.addWidget(self.test_group)
def resizeEvent(self, event):
scrollarea_vpsize = self.grid_scrollarea.viewport().size()
scrollarea_visible_size = self.grid_scrollarea.rect()
desired_width = scrollarea_vpsize.width()
desired_height = scrollarea_visible_size.height()
desired_height = desired_height - scrollarea_vpsize.height()
new_geom = QRect(0,0,desired_width+1,desired_height-1)
self.headings_widget.setGeometry(new_geom)
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
if __name__ == '__main__':
main()
これらの線に沿った何かは、あなたが指していた方法ですか?ウィンドウがユーザーによってサイズ変更される前の奇妙な初期動作を除いて、すべてが私が目指したとおりに期待されているように、すべてが期待されています。私はおそらくもう一度考えすぎたり、少なくとも何かを見落としています...何か考えはありますか?
解決
あなたは物事を少し考えすぎているかもしれません。
必要なのは、Scrollareaのビューポートのジオメトリと現在のマージンを使用して、マージンに配置するウィジェットのジオメトリを計算することです。
これらのウィジェットのジオメトリも、 resizeEvent
巻物の。
ソースコードを見ると QTableView
, 、この方法を使用してヘッダービュー(または非常によく似たもの)を管理することになると思います。
編集
あなたのテストケースの軽微なサイズ変更問題に対処するために、私はあなたに読むことをお勧めします 座標 ドキュメントのセクション QRect
(特に、3番目の段落以降)。
このようなテストケースを書き換えることで、より正確なサイズを取得することができました。
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setMinimumSize(640, 480)
self.container_widget = QWidget()
self.container_layout = QVBoxLayout()
self.container_widget.setLayout(self.container_layout)
self.setCentralWidget(self.container_widget)
self.grid_scrollarea = ScrollArea(self)
self.test_group = QGroupBox("Results")
self.test_layout = QVBoxLayout()
self.test_group.setLayout(self.test_layout)
self.test_layout.addWidget(self.grid_scrollarea)
self.container_layout.addWidget(self.test_group)
class ScrollArea(QScrollArea):
def __init__(self, parent=None):
QScrollArea.__init__(self, parent)
self.grid_widget = QWidget()
self.grid_layout = QGridLayout()
self.grid_widget.setLayout(self.grid_layout)
self.setWidgetResizable(True)
self.setWidget(self.grid_widget)
# save the margin values
self.margins = QMargins(0, 30, 0, 0)
self.setViewportMargins(self.margins)
self.headings_widget = QWidget(self)
self.headings_layout = QGridLayout()
self.headings_widget.setLayout(self.headings_layout)
self.headings_layout.setContentsMargins(1,1,0,0)
self.heading_label1 = QLabel("Column 1")
self.heading_label1.setContentsMargins(16,0,0,0)
self.heading_label2 = QLabel("Col 2")
self.heading_label2.setAlignment(Qt.AlignCenter)
self.heading_label2.setMaximumWidth(65)
self.heading_label3 = QLabel("Column 3")
self.heading_label3.setContentsMargins(8,0,0,0)
self.headings_layout.addWidget(self.heading_label1,0,0)
self.headings_layout.addWidget(self.heading_label2,0,1)
self.headings_layout.addWidget(self.heading_label3,0,2)
self.headings_widget.setStyleSheet(
"background: green; border-bottom: 1px solid black;" )
rows_to_add = 10
for i in range(rows_to_add):
col1 = QLineEdit()
col2 = QLineEdit()
col2.setMaximumWidth(65)
col3 = QLineEdit()
row = self.grid_layout.rowCount()
self.grid_layout.addWidget(col1,row,0)
self.grid_layout.addWidget(col2,row,1)
self.grid_layout.addWidget(col3,row,2)
def resizeEvent(self, event):
rect = self.viewport().geometry()
self.headings_widget.setGeometry(
rect.x(), rect.y() - self.margins.top(),
rect.width() - 1, self.margins.top())
QScrollArea.resizeEvent(self, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
他のヒント
私は同様の問題を抱えていて、それを少し違った方法で解決しました。使用する代わりに QSCROLLAREA 下部スクロール領域の動きを2つ使用し、上部の領域に移動します。以下のコードが行うことです
- QVBoxLayoutに2つのQSCROLLAREAウィジェットを作成します。
- 上部のQScrollareaのスクロールバーの可視性を無効にし、固定された高さを割り当てます。
- を使用して ValueChanged 下部qscrollaeaの水平スクロールバーの信号は、下部qscrollareaから上部1に水平スクロールバーの値を「転送」することができ、窓の上部に固定ヘッダーが固定されます。
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
widget = QWidget()
self.setCentralWidget(widget)
vLayout = QVBoxLayout()
widget.setLayout(vLayout)
# TOP
scrollAreaTop = QScrollArea()
scrollAreaTop.setWidgetResizable(True)
scrollAreaTop.setFixedHeight(30)
scrollAreaTop.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scrollAreaTop.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scrollAreaTop.setWidget(QLabel(" ".join([str(i) for i in range(100)])))
# BOTTOM
scrollAreaBottom = QScrollArea()
scrollAreaBottom.setWidgetResizable(True)
scrollAreaBottom.setWidget(QLabel("\n".join([" ".join([str(i) for i in range(100)]) for _ in range(10)])))
scrollAreaBottom.horizontalScrollBar().valueChanged.connect(lambda value: scrollAreaTop.horizontalScrollBar().setValue(value))
vLayout.addWidget(scrollAreaTop)
vLayout.addWidget(scrollAreaBottom)