I am trying to create a simple gui that displays the (memory) layout of some components of a device, but I am having a really hard time enforcing the policy I want to the displayed area.
Let me first show what I have so far (my code became quite large, but I changed/narrowed the code down to the minimal required for anyone to be able to run it):
#!/usr/local/bin/python3
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Register(QGraphicsRectItem):
RegisterSize = 125
NameColor = QColor(Qt.blue)
ValueColor = QColor(0, 154, 205)
def __init__(self, name, value, pos, parent = None):
super(Register, self).__init__(parent)
self.setPos(pos)
self.width = Register.RegisterSize
self.height = 0
self.set_register_name(name)
self.set_register_value(value)
self.setRect(0, 0, self.width, self.height)
def set_register_name(self, name):
self.text_item = QGraphicsTextItem(name, self)
self.text_item.setDefaultTextColor(Register.NameColor)
self.height += self.text_item.boundingRect().height()
def set_register_value(self, value):
self.value_item = QGraphicsTextItem(str(value), self)
self.value_item.setDefaultTextColor(Register.ValueColor)
self.value_item.setPos(self.text_item.boundingRect().bottomLeft())
self.height += self.value_item.boundingRect().height()
class Title(QGraphicsTextItem):
TitleFont = 'Times New Roman'
TitleFontSize = 18
TitleColor = QColor(Qt.red)
def __init__(self, title, parent = None):
super(Title, self).__init__(title, parent)
self.setFont(QFont(Title.TitleFont, Title.TitleFontSize))
self.setDefaultTextColor(Title.TitleColor)
class Component(QGraphicsItem):
def __init__(self, parent = None):
super(Component, self).__init__(parent)
self.width = Register.RegisterSize * 4
self.height = 0
self.add_title()
self.add_registers()
self.rect = QRectF(0, 0, self.width, self.height)
def add_title(self):
self.title = Title('Component Layout', self)
self.title.setPos((self.width - self.title.boundingRect().width()) / 2, 0)
self.height += self.title.boundingRect().height()
def add_registers(self):
y_coor = self.height
x_coor = 0
for i in range(64):
register = Register('register {0:d}'.format(i), i, QPointF(x_coor, y_coor), self)
x_coor = ((i + 1) % 4) * Register.RegisterSize
if (i + 1) % 4 == 0:
y_coor += register.rect().height()
self.height = y_coor
def boundingRect(self):
return self.rect.adjusted(-1, -1, 1, 1)
def paint(self, painter, option, widget):
pen = QPen(Qt.blue)
painter.setPen(pen)
painter.drawRect(self.rect)
class Device(QGraphicsItem):
LeftMargin = 50
RightMargin = 50
TopMargin = 20
BottomMargin = 20
def __init__(self, parent = None):
super(Device, self).__init__(parent)
self.width = Device.LeftMargin + Device.RightMargin
self.height = Device.TopMargin + Device.BottomMargin
component = Component(self)
component.setPos(QPointF(Device.LeftMargin, Device.TopMargin))
self.width += component.boundingRect().width()
self.height += component.boundingRect().height()
self.rect = QRectF(0, 0, self.width, self.height)
def paint(self, painter, option, widget):
pass
def boundingRect(self):
return self.rect.adjusted(-1, -1, 1, 1)
class MainForm(QDialog):
def __init__(self, parent = None):
super(MainForm, self).__init__(parent)
self.scene = QGraphicsScene(parent)
self.view = QGraphicsView(self)
self.view.setScene(self.scene)
self.scene.addItem(Device())
self.resize(700, 900)
def run_app():
app = QApplication(sys.argv)
form = MainForm()
form.show()
app.exec_()
if __name__ == '__main__':
run_app()
This code, when launched, displays the following:
I don't mind the vertical scrollbar, since I intend to add more Components to the Device, and they won't all fit, what bothers me is the horizontal scrollbar.
Why does it appear without me explicitly asking?
It's not like there's no room in the window for the QGraphicsView
to display the content.
Moreover, I noticed that the horizontal (and vertical) scrollbars do not appear, when only the Component is added to the QGraphicsView
:
self.scene.addItem(Component()) # << previously was self.scene.addItem(Device())
Now the scrollbars do not appear:
Also; when I instead change the following lines:
LeftMargin = 0 # previously was 50
RightMargin = 0 # previously was 50
TopMargin = 0 # previously was 20
BottomMargin = 0 # previously was 20
Scrollbars do not appear. (I probably crossed some boundary with these margins added?)
I know I can control the scrollbars policy with the QGraphicsView.setHorizontalScrollBarPolicy()
to make the horizontal scrollbar always off, but that raises another problem: When there's no way to scroll right, the vertical scrollbar "steals" some of the pixels from the display, making Device.RightMargin != Device.LeftMargin
. Also, I am curious about what's the size boundary above which the horizontal/vertical scrollbars appear.
So, this is the policy I want to enforce:
I want the displayed area to always have a minimum height of X pixels (regardless of
Device()
's height), and for vertical scrollbar to appear only if theDevice()
height passes these X pixels boundary (I'll determineDevice()
's height by summing allComponent()
s heights)I want QGraphicsView to never show horizontal scrollbar (the width
Device()
's width is fixed and independent of the number ofComponent()
s).- Whenever vertical scrollbar is needed, I don't want it to take up pixels from my display area.
- I want to know what is the boundary (in pixels) above which scrollbars will appear (when I don't specify scrollbar policy).
EDIT:
After playing with it a bit, I figured something:
The unwanted horizontal scroll bar appears only because the vertical one appears and steals some of the display space.
According to the doc, the default policy for the horizontal scroll bar is
Qt::ScrollBarAsNeeded
, which means: "shows a scroll bar when the content is too large to fit and not otherwise.", but it doesn't state what is considered "too large".
When I played around with the margins (Device.TopMargin
/Device.BottomMargin
), I discovered that the vertical scroll bar appears (and consequently the horizontal one) whenDevice.boundingRect().height()
crosses the 786 pixels boundary.
I couldn't figure out where did this number came from or how to control it.