How to inherit from QWidget in PyQt? - python-2.7

Maigcally I can't inherit from QWidget using PyQt 4:
from PyQt4.QtGui import QApplication, QMainWindow, QWidget
class MyWidget(QWidget):
pass
if __name__ == "__main__":
app = QApplication([])
window = QMainWindow()
window.resize(200, 200)
widget1 = MyWidget(window)
widget1.resize(100, 100)
widget1.setStyleSheet("background-color:#FFFFFF")
window.show()
app.exec_()
It doesn't work. I just can't see the widget. But, using QLabel instead of QWidget works.
Thanks.
Solution
Reimplement paintEvent:
class MyWidget(QWidget):
def paintEvent(self, event):
o = QStyleOption()
o.initFrom(self)
p = QPainter(self)
self.style().drawPrimitive(QStyle.PE_Widget, o, p, self)

You are subclassing correctly. The "problem" is just that the QWidget has the same background colour as the main window (your call to setStyleSheet is not working).
As proof, run this code from a terminal:
from PyQt4.QtGui import QApplication, QMainWindow, QWidget, QLabel
class MyWidget(QWidget):
def enterEvent(self, evt):
print 'a'
if __name__ == "__main__":
app = QApplication([])
window = QMainWindow()
window.resize(200, 200)
widget1 = MyWidget(window)
widget1.resize(100, 100)
widget1.setStyleSheet("background-color:#FFFFFF")
window.show()
app.exec_()
When you move your mouse into the first 100,100 pixel square, you will see the letter 'a' being printed in the terminal

Related

Getting conversion error QStyleOptionSpinBox to const QStyleOptionComplex*

I'm subclassing QSpinBox and trying to get the sub controls up/down arrows rectangle sizes as demonstrated here with python and pyside. However I get errors when trying to convert my code over to C++.
Here is working python example I'm trying to convert to Qt/C++
import sys
import os
from PySide import QtGui, QtCore
class SuperSpinner(QtGui.QSpinBox):
def __init__(self, parent=None):
super(SuperSpinner, self).__init__(parent)
def contextMenuEvent(self, event):
opt = QtGui.QStyleOptionSpinBox()
self.initStyleOption(opt)
r = QtCore.QRect()
for sc in (QtGui.QStyle.SC_SpinBoxUp, QtGui.QStyle.SC_SpinBoxDown):
r= r.united(self.style().subControlRect(QtGui.QStyle.CC_SpinBox, opt, sc, self))
if r.contains(event.pos()):
self.setValue(0)
self.selectAll()
else:
super(self.__class__, self).contextMenuEvent(event)
def main():
app = QtGui.QApplication(sys.argv)
ex = SuperSpinner()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The Error Message in Qt when attempting to compile
Main bits of CPP file:
#include "intsuperspinbox.h"
#include <QMouseEvent>
#include <QStyleOptionSpinBox>
#include <QCommonStyle>
#include <QStyle>
#include <QDebug>
IntSuperSpinBox::IntSuperSpinBox(QWidget *parent) :
QSpinBox(parent)
{
setMouseTracking(true);
}
void IntSuperSpinBox::contextMenuEvent(QContextMenuEvent *event)
{
QStyleOptionSpinBox opt;
opt.initFrom(this);
// ERRORS HERE
QRect upRect(this->style()->subControlRect(QStyle::CC_SpinBox, opt, QStyle::SC_SpinBoxUp, this));
QSpinBox::contextMenuEvent(event);
}
The second parameter to QStyle::subControlRect should be of type QStyleOptionComplex* so you need...
QRect upRect(this->style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxUp, this));
Note &opt rather than simply opt.

Why is the selection border of a QGraphicsWidget not visible in QGraphicsScene?

I have added a widget to a graphic scene (QGraphicScene) through a QGraphicsProxyWidget. The problem is that when I select the item it's selected, but the selection border is not visible.
Here is the code:
QDial *dial= new QDial(); // Widget
dial->setGeometry(3,3,100,100);// setting offset for graphicswidget and widget
QGraphicsWidget *ParentWidget = new QGraphicsWidget();// created to move and select on scene
ParentWidget->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
Scene->addItem(ParentWidget); // adding to scene
QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget();// adding normal widget through this one
proxy->setWidget( DialBox );
proxy->setParentItem(ParentWidget);
Here is the output:
How could I fix this?
Cause
QGraphicsWidget does not paint anything (including a selection rectangle), as seen from the source code:
void QGraphicsWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(painter);
Q_UNUSED(option);
Q_UNUSED(widget);
}
QGraphicsRectItem, however, does (see the source code):
void QGraphicsRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
...
if (option->state & QStyle::State_Selected)
qt_graphicsItem_highlightSelected(this, painter, option);
}
Solution
My solution would be to use QGraphicsRectItem instead of QGraphicsWidget as a handle to select/move the dial like this:
auto *dial= new QDial(); // The widget
auto *handle = new QGraphicsRectItem(QRect(0, 0, 120, 120)); // Created to move and select on scene
auto *proxy = new QGraphicsProxyWidget(handle); // Adding the widget through the proxy
dial->setGeometry(0, 0, 100, 100);
dial->move(10, 10);
proxy->setWidget(dial);
handle->setPen(QPen(Qt::transparent));
handle->setBrush(Qt::gray);
handle->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
Scene->addItem(handle); // adding to scene
This code produces the following result:
Click the dark gray area around the dial widget to select/move it or the widget itself in order to interact with it.
I know this question is old but here is a python code for anyone looking for the same. It will draw a border whenever the widget is clicked.
class rectangle(QtWidgets.QGraphicsProxyWidget):
def __init__(self):
super().__init__()
self.container = Container() # a class inheriting Qwidget class
self.setWidget(self.container)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
def paint(self, qp, opt, widget):
qp.save()
pen = QtGui.QPen()
pen.setColor(Qt.transparent)
pen.setWidth(3)
if self.isSelected():
pen.setColor(Qt.yellow) # selection color of your choice
qp.setBrush(Qt.transparent)
qp.setPen(pen)
qp.drawRect(0, 0, self.container.width(), self.container.height()) # draws rectangle around the widget
super().paint(qp, opt, widget)
qp.restore()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.setSelected(True)
super().mousePressEvent(event)

How to lock all toolbars via menu in Qt?

I am experimenting with Qt Creator and the Application Example.
I would like to add a checkable menu entry to a toolbar menu that reads "Lock toolbars" and, when checked, locks the positions of all tool bars. I guess that it is a quite common feature.
I have managed to find a command that locks single bars via :
toolBar->setMovable(false);
But I cannot figure out how to lock all toolbars.
Edit
This question used to contain an inquiry concerning the toolbar context menu rather than the standard menu. Since I got an answer concerning the context menu elsewhere I removed it from this question.
How to add an entry to toolbar context menu in qt?
Here is an example of how you can achieve it. First, add a QAction and a QMenu; also, declare all your toolbars private :
private:
QMenu* m_pLockMenu;
QToolBar* m_pFileToolBar;
QToolBar* m_pEditToolBar;
QToolBar* m_pHelpToolBar;
QAction* m_pLockAction;
Also, declare a slot where you will manage the locking of your toolbars when the action will be triggered :
public slots :
void lockActionTriggered();
Implement your slot. You just need to lock all the toolbars :
void lockActionTriggered()
{
m_pFileToolBar->setMovable(false);
m_pEditToolbar->setMovable(false);
m_pHelpToolBar->setMovable(false);
}
Now, you just have to declare your main window in your .cpp, and add the menu, the toolbars and the action in it :
QMainWindow* mainWindow = new QMainWindow();
m_pLockMenu = mainWindow->menuBar()->addMenu("Lock Toolbars");
m_pFileToolBar = mainWindow->addToolBar("File");
m_pEditToolBar = mainWindow->addToolBar("Edit");
m_pHelpToolBar = mainWindow->addToolBar("Help");
m_pLockAction = new QAction("Lock", this);
Now, add the action to the menu :
m_pLockMenu->addAction(m_pLockAction);
And connect the QAction's signal triggered() to your slot :
connect(m_pLockAction, SIGNAL(triggered()), this, SLOT(lockActionTriggered()));
Don't forget to show() your main window :
mainWindow->show();
And it should be working now!
EDIT
Your code must look like this :
In the mainwindow.h :
class MainWindow : public QMainWindow
{
...
private:
QMenu* m_pLockMenu;
QToolBar* m_pFileToolBar;
QToolBar* m_pEditToolBar;
QToolBar* m_pHelpToolBar;
QAction* m_pLockAction;
public slots :
void lockActionTriggered();
};
In the main.cpp:
int main(int argc, char *argv[])
{
...
QApplication app(argc, argv);
MainWindow window;
window.show();
app.exec();
}
In the mainwindow.cpp:
void MainWindow::createActions()
{
m_pLockMenu = menuBar()->addMenu("Lock Toolbars");
m_pFileToolBar = addToolBar("File");
m_pEditToolBar = addToolBar("Edit");
m_pHelpToolBar = addToolBar("Help");
m_pLockAction = new QAction("Lock", this);
m_pLockMenu->addAction(m_pLockAction);
connect(m_pLockAction, SIGNAL(triggered()), this, SLOT(lockActionTriggered()));
...
}
void MainWindow::lockActionTriggered()
{
m_pFileToolBar->setMovable(false);
m_pEditToolbar->setMovable(false);
m_pHelpToolBar->setMovable(false);
}
If you have a pointer to your mainwindow, you can simply find all the toolbars and iterate over them:
// in slot
for (auto *t: mainWindow->findChildren<QToolBar*>())
t->setMovable(false);
Alternatively, you might want to connect all toolbars to a single toggle action, when you initialise the UI:
// in constructor
for (auto *t: this->findChildren<QToolBar*>())
connect(action, &QAction::toggled, t, &QToolBar::setMovable);
One common example with flexible lock options
# Created by BaiJiFeiLong#gmail.com at 2022/2/15 22:56
import typing
from PySide2 import QtWidgets, QtGui, QtCore
class Window(QtWidgets.QMainWindow):
def createPopupMenu(self) -> QtWidgets.QMenu:
menu = super().createPopupMenu()
menu.addSeparator()
toolBars: typing.Sequence[QtWidgets.QToolBar] = self.findChildren(QtWidgets.QToolBar)
for toolbar in toolBars:
if toolbar.rect().contains(toolbar.mapFromGlobal(QtGui.QCursor.pos())):
title = "%s %s" % ("Lock" if toolbar.isMovable() else "Unlock", toolbar.windowTitle())
menu.addAction(title, lambda toolbar=toolbar: toolbar.setMovable(not toolbar.isMovable()))
menu.addSeparator()
if any(x.isMovable() for x in toolBars):
menu.addAction("Lock All", lambda: list(x.setMovable(False) for x in toolBars))
if any(not x.isMovable() for x in toolBars):
menu.addAction("Unlock All", lambda: list(x.setMovable(True) for x in toolBars))
return menu
app = QtWidgets.QApplication()
window = Window()
window.setWindowTitle("Toolbar Example")
for i in range(1, 6):
toolBar = window.addToolBar(f"ToolBar{i}")
toolBar.setMovable(i % 2 == 0)
toolBar.addWidget(QtWidgets.QPushButton(f"ToolBar {i}"))
label = QtWidgets.QLabel("Ready")
label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
window.setCentralWidget(label)
window.resize(800, 600)
window.show()
app.exec_()

How to make wxPython, DC background transparent?

How can i make the default white DC background become invisible (transparent)?
Run the following example, it shows a white background over a button. I would like to remove the white background of the DC. (to show only the red X)
import wx
class drawover(wx.Window):
def __init__(self, parent):
wx.Window.__init__(self, parent)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
self.Bind(wx.EVT_SET_FOCUS, self.OnFocus)
def OnFocus(self, evt):
self.GetParent().SetFocus()
def OnErase(self, evt):
pass
def OnPaint(self, evt):
dc = wx.PaintDC(self)
dc.BeginDrawing()
dc.SetPen(wx.Pen("RED", 1))
dc.DrawLineList([(0,0,100,100), (100,0,0,100)])
dc.EndDrawing()
class frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, size=(600,600))
s = wx.BoxSizer(wx.VERTICAL)
self.tc1 = wx.TextCtrl(self, style=wx.TE_MULTILINE)
s.Add(self.tc1, 1, wx.EXPAND)
self.tc2 = wx.Button(self)
s.Add(self.tc2, 1, wx.EXPAND)
self.d = drawover(self.tc2)
self.tc2.Bind(wx.EVT_SIZE, self.OnSize2)
self.SetSizer(s)
self.Layout()
def OnSize2(self, evt):
self.d.SetSize((101,101))
if __name__ == '__main__':
a = wx.App(0)
b = frame()
b.Show(1)
a.MainLoop()
http://wiki.wxpython.org/Transparent%20Frames
If you are on windows just see above.
If not you basically have to capture the frame below it as an image and paint it (in your onpaint) to fake it
also see
http://wxpython-users.1045709.n5.nabble.com/Transparent-Panels-td2303275.html

Qt - QPushButton text formatting

I have a QPushButton and on that I have a text and and icon. I want to make the text on the button to be bold and red. Looked at other forums, googled and lost my hope. Seems there is no way to do that if the button has an icon (of course if you don't create a new icon which is text+former icon). Is that the only way? Anyone has a better idea?
You really don't need to subclass to change the formatting of your button, rather use stylesheets e.g.
QPushButton {
font-size: 18pt;
font-weight: bold;
color: #ff0000;
}
Applying this to the button that you want to change will make the buttons text 18pt, bold and red. You can apply via widget->setStyleSheet()
Applying this to a widget in the hierarchy above will style all the buttons underneath, the QT stylesheet mechanism is very flexible and fairly well documented.
You can set stylesheets in the designer too, this will style the widget that you are editing immediately
you make the subclass of "QPushbutton", then override the paint event,
there you modify the text to your wish.
here it is,
class button : public QPushButton
{
Q_OBJECT
public:
button(QWidget *parent = 0)
{
}
~button()
{
}
void paintEvent(QPaintEvent *p2)
{
QPushButton::paintEvent(p2);
QPainter paint(this);
paint.save();
QFont sub(QApplication::font());
sub.setPointSize(sub.pointSize() + 7);
paint.setFont(sub);
paint.drawText(QPoint(300,300),"Hi");
paint.restore();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
button b1;
b1.showMaximized();
return a.exec();
}
Note:My answer is inspired by the idea from #Петър Петров and the comment from #mjwach.
You can subclass QPushButton and give it two private fields:
self.__lbl: A QLabel() instance to hold the rich text. Its background is made transparent and it doesn't catch mouse events.
self.__lyt: A QHBoxLayout() to hold the label. The layout margins are set to zero, such that the label's borders touch the button's borders. In other words: we ensure that the label has the exact same size as the button, and is positioned right on top of it.
Finally you have to override the setText() method, such that the text ends up in the label instead of the button.
class RichTextPushButton(QPushButton):
def __init__(self, parent=None, text=None):
if parent is not None:
super().__init__(parent)
else:
super().__init__()
self.__lbl = QLabel(self)
if text is not None:
self.__lbl.setText(text)
self.__lyt = QHBoxLayout()
self.__lyt.setContentsMargins(0, 0, 0, 0)
self.__lyt.setSpacing(0)
self.setLayout(self.__lyt)
self.__lbl.setAttribute(Qt.WA_TranslucentBackground)
self.__lbl.setAttribute(Qt.WA_TransparentForMouseEvents)
self.__lbl.setSizePolicy(
QSizePolicy.Expanding,
QSizePolicy.Expanding,
)
self.__lbl.setTextFormat(Qt.RichText)
self.__lyt.addWidget(self.__lbl)
return
def setText(self, text):
self.__lbl.setText(text)
self.updateGeometry()
return
def sizeHint(self):
s = QPushButton.sizeHint(self)
w = self.__lbl.sizeHint()
s.setWidth(w.width())
s.setHeight(w.height())
return s
You can subclass a QLabel and completely throw away all of its mouse events (so they reach the parent).
Then crate a QPushButton, set a layout on it and in this layout insert a QLabel with formatted text. You get a button that contains QLabel and is clickable. (Any Qt Widget can have layout set, including ones that you never ever expected they can.)