QTextEdit expand until fully visible - qt

I have a QDialog(QVBoxLayout) with the following widgets in order: QTextEdit, QLabel, [layout stretch], QPushButton
The problem is that I would like the QTextEdit to expand with the QDialog, but only until the scrollbar disappears, after which the layout stretch should start expanding, revealing a gap between the QLabel and the QPushButton.
What would be a sensible way to go about this?
Note: The QDialog is resizable and the QTextEdit has word wrapping on.
Edit: For Qt4/PySide, the following should work
class TextEdit(qt.QTextEdit):
def __init__(self, *args, **kwargs):
qt.QTextEdit.__init__(self, *args, **kwargs)
self.document().modificationChanged.connect(self.updateMaxHeight)
def updateMaxHeight(self, *args):
self.setMaximumHeight(self.document().size().height())
def resizeEvent(self, e):
qt.QTextEdit.resizeEvent(self, e)
self.updateMaxHeight()
class MyDialog(qt.QDialog):
def __init__(self, *args, **kwargs):
qt.QDialog.__init__(self, *args, **kwargs)
self.setLayout(qt.QVBoxLayout())
self.textEdit = TextEdit('Example text')
self.layout().addWidget(self.textEdit, 1) #1 for resizable
self.layout().addWidget(qt.QLabel('Example label')
self.layout().addStretch()
self.layout().addWidget(qt.QPushButton('Example button')

This is more difficult than it seems:
class TextEdit : public QTextEdit
{
public:
TextEdit(QWidget *parent = nullptr)
: QTextEdit(parent)
{
setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
connect(this, &TextEdit::textChanged, this, &TextEdit::updateGeometry);
}
QSize viewportSizeHint() const override
{
return document()->size().toSize();
}
};
The idea is to make the viewport size hint correspond to the size of the document and make the scroll area adjust to the size of the viewport. You also have to make sure the the layout gets notified when new lines are added to the document.
The final piece of the puzzle is to give your stretch item a bigger stretch factor so that it doesn't start sharing space with the editor:
layout->addStretch(1);

Related

Qt Transparent Background Widget

I want to make my own custom tooltip. So I create a class like below.
def __init__(self, parent=None):
QWidget.__init__(self, parent, Qt.ToolTip)
self.setAttribute(Qt.WA_TranslucentBackground)
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHints(QPainter.Antialiasing)
rect = self.rect()
path = QPainterPath()
path.setFillRule(Qt.WindingFill)
path.addRoundedRect(rect.adjusted(self.PADDING+self.ARROW_DEPTH, self.PADDING, -self.PADDING, -self.PADDING), self.ARROW_DEPTH, self.ARROW_DEPTH)
path.moveTo(self.PADDING, rect.height()/2)
path.lineTo(self.PADDING+self.ARROW_DEPTH, rect.height()/2 - self.ARROW_OFFSET)
path.lineTo(self.PADDING+self.ARROW_DEPTH, rect.height()/2 + self.ARROW_OFFSET)
path.lineTo(self.PADDING, rect.height()/2)
painter.setPen(Qt.NoPen)
painter.setBrush(Qt.red)
painter.drawPath(path)
But the result is...
How can I make the black region be transparent?
SOLUTION
QWidget.__init__(self, parent, Qt.ToolTip)
self.setWindowFlags(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
I add a line self.setWindowFlags(Qt.FramelessWindowHint), and it works.

Subclassed QFrame not adjusting to it's layout

I'm trying to include a horizontal frame containing a label inside a vertical frame, but even though the label is displayed it's not in the right position and it's limited to a size of a standard QLabel
This is the main class:
class Launcher(QMainWindow):
def __init__(self):
super().__init__()
self.setFrame() #sets up window's geometry, works fine
self.setContent()
self.show()
def setContent(self):
layout = QBoxLayout(QBoxLayout.TopToBottom)
layout.addWidget(widgets.Logo(self), 0, Qt.AlignTop)
self.setLayout(layout)
And this is the imported class from a "widgets" module
class Logo(QFrame):
def __init__(self, parent):
super().__init__(parent)
layout = QBoxLayout(QBoxLayout.LeftToRight)
text = QLabel("PyTitle", self)
text.setAlignment(Qt.AlignCenter)
text.setFont(QFont("impact", 48))
layout.addWidget(text, 0, Qt.AlignCenter)
self.setLayout(layout)
self.show()
The result is this:
If I forcefully resize both QLabel AND QFrame, it's visible, but still in the top-left.
You must not set a layout on a QMainWindow, because it already has one built in (to handle dock-widgets, the menu-bar, status-bar, etc).
Instead, set a central-widget, and add all the widgets and layouts to that:
class Launcher(QMainWindow):
...
def setContent(self):
widget = widgets.Logo(self)
self.setCentralWidget(widget)
(PS: you only need to call show() on the top-level window - for all other child widgets, it's redundant).

Why I do have strange extra space?

I have a SingleTweetWidget to display a tweet.
If I put it into a QScrollArea, everything is working fine.
class TweetListWidget(QtGui.QWidget):
def __init__(self, client=None, parent=None):
super(TweetListWidget, self).__init__(parent)
self.setupUi()
def setupUi(self):
self.layout = QtGui.QVBoxLayout(self)
self.setLayout(self.layout)
def setModel(self, model):
self.model = model
self.model.rowsInserted.connect(self._rowsInserted)
def _rowsInserted(self, parent, start, end):
for index in range(start, end + 1):
item = self.model.get_item(index)
widget = SingleTweetWidget(self.client, item)
self.layout.insertWidget(index, widget)
But, if I put it into a dialog, there will be some extra space.
def setupUi(self, widget):
super(NewpostWindow, self).setupUi(widget)
tweet = SingleTweetWidget(self.client, self.tweet, self)
self.verticalLayout.insertWidget(0, tweet)
Please notice the space between the time (6s ago) and the blue separator line.
Where is it come from? I have no idea with it.
By the way, you can get the source code of SingleTweetWidget from https://github.com/WeCase/WeCase/blob/dev-0.06/src/TweetListWidget.py
QDialog has a layout which put a vertical space between widgets. It's because the default minimum height of QDialog is higher than the height of the two widgets. You can use self->setMinimumHeight(int) and self->setMaximumHeight(int) and the width variants or self->setFixedSize(w,h), etc...
You can set max/min width/height with every widget.
Read something about QLayout, QDialog and the Qt and see some examples. Qt have very good documentation. See
http://qt-project.org/doc/
http://qt-project.org/doc/qt-4.8/examples-layouts.html
http://qt-project.org/doc/qt-4.8/qwidget.html#setFixedSize

Painting in a QTextEdit without affecting the text

I am trying to paint some lines in a QTextEdit but when paintEvent it is called the whole QTextEdit text clears, the lines are drawn, no further text input possible. If I scroll, the drawn lines act very weird, somehow multiply on horizontal or vertical. I want to paint on the QTextEdit w/o affecting it's text and the painted stuff to act normal when scrolling, to keep its coordinates.
Here is the code:
class TextEdit(QTextEdit):
def __init__(self, parent = None):
super(TextEdit, self).__init__(parent)
self.setViewportMargins(10, 0, 0, 0)
def paintEvent(self, event):
painter = QPainter(self.viewport())
painter.drawLine(10, 10, 200, 10)
Add this to the bottom of your paintEvent method:
super(TextEdit, self).paintEvent(event)

Qt How to disable mouse scrolling of QComboBox?

I have some embedded QComboBox in a QTableView. To make them show by default I made those indexes "persistent editor". But now every time I do a mouse scroll on top them they break my current table selection.
So basically how can I disable mouse scrolling of QComboBox?
As I found this question, when I tried to figure out the solution to (basically) the same issue: In my case I wanted to have a QComboBox in a QScrollArea in pyside (python QT lib).
Here my redefined QComboBox class:
#this combo box scrolls only if opend before.
#if the mouse is over the combobox and the mousewheel is turned,
# the mousewheel event of the scrollWidget is triggered
class MyQComboBox(QtGui.QComboBox):
def __init__(self, scrollWidget=None, *args, **kwargs):
super(MyQComboBox, self).__init__(*args, **kwargs)
self.scrollWidget=scrollWidget
self.setFocusPolicy(QtCore.Qt.StrongFocus)
def wheelEvent(self, *args, **kwargs):
if self.hasFocus():
return QtGui.QComboBox.wheelEvent(self, *args, **kwargs)
else:
return self.scrollWidget.wheelEvent(*args, **kwargs)
which is callable in this way:
self.scrollArea = QtGui.QScrollArea(self)
self.frmScroll = QtGui.QFrame(self.scrollArea)
cmbOption = MyQComboBox(self.frmScroll)
It is basically emkey08's answer in the link Ralph Tandetzky pointed out, but this time in python.
The same can happen to you in a QSpinBox or QDoubleSpinBox. On QSpinBox inside a QScrollArea: How to prevent Spin Box from stealing focus when scrolling? you can find a really good and well explained solution to the problem with code snippets.
You should be able to disable mouse wheel scroll by installing eventFilter on your QComboBox and ignore the events generated by mouse wheel, or subclass QComboBox and redefine wheelEvent to do nothing.
c++ version of Markus ansver
class FocusWhellComboBox : public QComboBox
{
public:
explicit FocusWhellComboBox(QWidget* parent = nullptr)
: QComboBox(parent)
{
this->setFocusPolicy(Qt::StrongFocus);
}
void wheelEvent(QWheelEvent* e) override
{
if (this->hasFocus()) {
QComboBox::wheelEvent(e);
}
}
};

Resources