I'm trying to use a QComboBox as a item delegate to update a field in my QTableView. The editor, selection, and model data are all working fine. However, the problem is that when I click on the cell, the QComboBox does not open inside the cell. Instead it opens somewhere outside the main GUI (see attached screenshot). Any ideas how I can fix this beahvior?
I have also added a QSpinBox for column Freq/Qty and they are showing up properly in the cell when I double click for editing.
Note the combo box on the top left of the image.
My code:
...
hb = QTableView()
hbm = HierBlocksDataModel()
hb.setModel(hbm)
hb.verticalHeader().setDefaultSectionSize(25)
lbd = LibraryBoxDelegate(hb)
hb.setItemDelegateForColumn(3, lbd)
...
class LibraryBoxDelegate(QStyledItemDelegate):
def __init__(self, parent, node=None):
super(LibraryBoxDelegate, self).__init__(parent)
self.node = node
def createEditor(self, parent, option, index):
if self.node is not None:
cb = QComboBox()
cb.addItems(list('abc'))
return cb
def setEditorData(self, editor, index):
editor.setCurrentText(index.model().data(index, Qt.DisplayRole))
def setModelData(self, editor, model, index):
model.setData(index, editor.currentText(), Qt.EditRole)
Related
By default if a cell is edited in a QTreeWidget, the editor changes its width based on length of text.
Is it possible to set the editorĀ“s width to fill the cell?
Here is the code to reproduce the screenshot:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Example(QTreeWidget):
def __init__(self):
super().__init__()
self.resize(600, 400)
self.setHeaderLabels(['Col1', 'Col2', 'Col3', 'Col4'])
self.setRootIsDecorated(False)
self.setAlternatingRowColors(True)
self.setSelectionBehavior(QAbstractItemView.SelectItems)
# self.setSelectionMode(QAbstractItemView.SingleSelection)
self.setStyleSheet('QTreeView { show-decoration-selected: 1;}')
for i in range(5):
item = QTreeWidgetItem(['hello', 'bello'])
item.setFlags(item.flags() | Qt.ItemIsEditable)
self.addTopLevelItem(item)
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You can create a simple QStyledItemDelegate and override its updateEditorGeometry() in order to always resize it to the index rectangle:
class FullSizedDelegate(QStyledItemDelegate):
def updateEditorGeometry(self, editor, opt, index):
editor.setGeometry(opt.rect)
class Example(QTreeWidget):
def __init__(self):
# ...
self.setItemDelegate(FullSizedDelegate(self))
** UPDATE **
The default text editor for all item views is an auto expanding QLineEdit, which tries to expand itself to the maximum available width (the right edge of the viewport) if the text is longer than the visual rectangle of the item. In order to avoid this behavior and always use the item rect, you have to return a standard QLineEdit. In this case the updateGeometry override is usually not necessary anymore (but I'd keep it anyway, as some styles might still prevent that):
class FullSizedDelegate(QStyledItemDelegate):
def createEditor(self, parent, opt, index):
if index.data() is None or isinstance(index.data(), str):
return QLineEdit(parent)
return super().createEditor(parent, opt, index)
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).
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
I am trying to create a main window (fixed size) that contains a QTableView, with QSpacerItems above and below, in order to centralise the table (vertically).
(Sorry, can't post an image, apparently).
I have a QVBoxLayout, into which I have a vertical spacer, the QTableView, and another vertical spacer. I've played with all combinations of QSizePolicy for all three widgets, but I cannot get the table to be displayed without scrollbars. (I cannot use Qt.ScrollBarAlwaysOff because they will be needed if the number of items exceeds the main window's size). So the vertical scrollbars on the QTableView are displayed, even though the vertical spacers are absorbing plenty of space between the view and the main window.
I want the vertical spacers to take up the minimum space required above and below the table widget in order to centralise the rows, and the table widget to display as many rows as possible, without scrollbars.
You can subclass QTableView, use QSizePolicy::Fixed in the vertical direction and override sizeHint() to return your preferred vertical height.
Here's a working example (You didn't specify language, so I am going to assume it is Python :-) :
import sys
from PySide import QtCore, QtGui
class MyTableView(QtGui.QTableView):
def __init__(self, parent=None):
super().__init__(parent)
#assume expanding in horizontal direction and fixed in vertica direction
self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
def sizeHint(self):
return QtCore.QSize(400, 500) #I allow you to edit that!
class MyApplication(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QtGui.QVBoxLayout()
table_view = MyTableView()
layout.addWidget(table_view)
self.model = QtGui.QStringListModel() #use a string list model for simplicity
table_view.setModel(self.model)
self.strings = ['1', '2', '3']
self.model.setStringList(self.strings) #initialize the model
self.counter = 4
button = QtGui.QPushButton('Add Cell') #this button updates the model and adds cells
button.clicked.connect(self.addCell)
layout.addWidget(button)
self.setLayout(layout)
def addCell(self):
self.strings.append(str(self.counter))
self.counter += 1
self.model.setStringList(self.strings)
app = QtGui.QApplication(sys.argv)
main = MyApplication()
main.show()
sys.exit(app.exec_())
I have to create one screen in Qt in which I have to show a remote having lots of buttons in it and when user clicks some button on actual remote, corresponding button in the image get highlighted. So what I have done is, I have used QLabel and set the remote image as background image and then I have put small rectangular label for each button and filled them with semi transparent color and when user click button in actual remote label color changes, but by using this method lot of labels are getting used making code looking inefficient, so I was thinking of drawing on QLabel (which has a remote as background image) over buttons.
Can anybody suggest me, which API of Qt should I use, and how to follow up on this?
I believe QGraphics is the correct route for a completely custom graphical interface, but if you want to try something that doesn't require you to change too much of your existing approach, you can do a widget with a custom paint event:
This is written in PyQt but you can easily translate to Qt
from PyQt4 import QtCore, QtGui
class LabelButton(QtGui.QWidget):
clicked = QtCore.pyqtSignal()
def __init__(self, labelStr, pixStr, parent=None):
super(LabelButton, self).__init__(parent)
self.label = labelStr
self.pix = QtGui.QPixmap(pixStr)
def paintEvent(self, event):
super(LabelButton, self).paintEvent(event)
rect = event.rect()
painter = QtGui.QPainter(self)
painter.drawPixmap(rect, self.pix)
pos = (rect.bottomLeft()+rect.bottomRight()) / 2
pos.setY(pos.y()-10)
painter.drawText(pos, self.label)
painter.end()
def mousePressEvent(self, event):
event.accept()
self.clicked.emit()
def handleClick():
print "CLICK"
if __name__ == "__main__":
app = QtGui.QApplication([])
widget = LabelButton("A Text Label", "myImage.png")
widget.resize(600,400)
widget.show()
widget.raise_()
widget.clicked.connect(handleClick)
app.exec_()
This is a rough example. You can get more fine tuned with the drawing of the text. This widget takes a label string, and a picture path, and will paint the picture as the background, and the text as a label. You can do any number of things with this custom widget in both the paint event, and with custom signals and events.
I have used this code to Draw over Image in Label:
Image is loaded in Ui and the Code is as follows In paintevent
void ColorTab::paintEvent(QPaintEvent *e){
ui->lbl_capture_img->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
ui->Lbl_color_tab_WG->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
Cap_Image = QImage(ui->lbl_capture_img->pixmap()->toImage());
Lbl_Image = QImage(ui->Lbl_color_tab_WG->pixmap()->toImage());
QPainter painter_Lbl(&Lbl_Image);
QPainter painter_Cap(&Cap_Image);
QPen pen(Qt::white, 5, Qt::DotLine, Qt::RoundCap, Qt::RoundJoin);
painter_Lbl.setPen(pen);
painter_Cap.setPen(pen);
painter_Lbl.drawPolygon(blinkRect_Lbl);
painter_Cap.drawPolygon(blinkRect_Cap);
ui->lbl_capture_img->setPixmap(QPixmap::fromImage(Cap_Image));
ui->Lbl_color_tab_WG->setPixmap(QPixmap::fromImage(Lbl_Image));
painter_Cap.end();
painter_Lbl.end();
}