I am trying to figure out how to create a custom expanding widget that is totally resizable, but with the ability of being able to freely place labels and buttons without the constraints of layouts.
here is a rough idea of what i would like to achieve:
The idea is I would use a QGroupBox with some kind of title set, inside it will be a layout of some kind to ensure the contents inside can be resized when the QGroupBox is resized, with a custom button with the image of an arrow placed ontop in the corner of the QGroupBox that totally ignores the layout of the QGroupBox. which means that the QGroupBox has to be contained in an area without a layout. unfortunately what this means is the QGroupBox will no longer be "resizable" because it is not following a layout. So i want to know how to do this.
so far I have this code:
from PyQt4 import QtCore, QtGui
import sys
class Window(QtGui.QWidget):
resized = QtCore.pyqtSignal()
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setWindowTitle('Test UI')
self.setMinimumSize(75, 50)
#self.setMaximumSize(250, 500)
self.oldWidth = 0.0
self.oldHeight = 0.0
self.oldButtonPosX = 0.0
self.oldButtonPosY = 0.0
self.resized.connect(self.someFunction)
position = QtGui.QCursor.pos()
self.move(position)
self.uiWidget = QtGui.QWidget()
self.freeWidget = QtGui.QWidget(self.uiWidget)
self.verticalLayout = QtGui.QVBoxLayout(self.freeWidget)
self.exportPushButton = QtGui.QPushButton(self.freeWidget)
self.exportPushButton.setText('Export')
self.verticalLayout.addWidget(self.exportPushButton)
self.exportPushButton2 = QtGui.QPushButton(self.freeWidget)
self.exportPushButton2.setText('Export')
self.verticalLayout.addWidget(self.exportPushButton2)
self.testButton = QtGui.QPushButton(self.uiWidget)
self.testButton.setText('.')
self.testButton.setGeometry(5,5,20,20)
#self.testButton = QtGui.QPushButton(self.uiWidget)
self.lyt = QtGui.QVBoxLayout()
self.lyt.addWidget(self.uiWidget)
self.setLayout(self.lyt)
#self.oldWidth = self.width()
#self.oldHeight = self.height()
def resizeEvent(self, event):
self.resized.emit()
self.oldWidth = self.width()
self.oldHeight = self.height()
self.oldButtonPosX = self.testButton.x()
self.oldButtonPosY = self.testButton.y()
return QtGui.QWidget.resizeEvent(self, event)
def someFunction(self):
newWidth = self.width() - 20.0
newHeight = self.height() - 20.0
self.freeWidget.setGeometry(0,0,newWidth,newHeight)
newButtonX = self.oldButtonPosX + (self.oldWidth - newWidth)
print self.oldButtonPosX
print 'self.oldWidth: %s' %self.oldWidth
print 'newWidth: %s' %newWidth
value = self.oldWidth-newWidth
print 'difference: %s' %str(value)
newButtonY = 20 + (self.oldHeight - newHeight)
#self.freeWidget.setGeometry(self.geometry)
#print 'Resizing label'
#
if self.oldButtonPosX != 0.0:
self.testButton.setGeometry(newButtonX,newButtonY,20,20)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
By incorporating this custom "resized" function I read about in another thread (and not setting a parent), the resizing of a widget itself seems possible without the need of it being in a Layout, but now I face an issue where the button that I want to overlay stays in its original position. WHat I would like it to do is follow with a widget while resizing, but also not necessarily having it contained in that widget's layout (you can see I sort have been finnicking to get this effect)
How can I achieve this?
UPDATE:
So I've managed to sort of figure out how the interactive resize/scaling works. Basically the widget's position increments 1 pixel depending on how many widgets are in a single layout. So I tried to replicate this effect (i have two widgets in this test layout, so in theory it should take a pixel change of 3 for the overlaying button to increment one pixel in position). it works somewhat, but I am noticing now that if i move the mouse too fast when dragging, the resize does not keep up, and in turn causes a misalignment. How do i go about this issue...?
from PyQt4 import QtCore, QtGui
import sys
#https://stackoverflow.com/questions/43126721/pyqt-detect-resizing-in-widget-window-resized-signal?rq=1
class Window(QtGui.QWidget):
resized = QtCore.pyqtSignal()
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setWindowTitle('Test UI')
self.setMinimumSize(100, 100)
#self.setMaximumSize(250, 500)
self.oldWidth = 0.0
self.oldHeight = 0.0
self.getOriginalDimensions = False
self.oldButtonPosX = 5.0
self.oldButtonPosY = 5.0
self.resized.connect(self.someFunction)
position = QtGui.QCursor.pos()
self.move(position)
self.uiWidget = QtGui.QWidget()
self.freeWidget = QtGui.QWidget(self.uiWidget)
self.verticalLayout = QtGui.QVBoxLayout(self.freeWidget)
self.exportPushButton = QtGui.QPushButton(self.freeWidget)
self.exportPushButton.setText('ExportV')
self.verticalLayout.addWidget(self.exportPushButton)
self.exportPushButton2 = QtGui.QPushButton(self.freeWidget)
self.exportPushButton2.setText('Export')
self.verticalLayout.addWidget(self.exportPushButton2)
self.testButton = QtGui.QPushButton(self.uiWidget)
self.testButton.setText('.')
self.testButton.setGeometry(5,5,20,20)
#self.testButton = QtGui.QPushButton(self.uiWidget)
self.lyt = QtGui.QVBoxLayout()
self.lyt.addWidget(self.uiWidget)
self.setLayout(self.lyt)
self.buttonYDifference = 0
#self.oldWidth = 100
#self.oldHeight = 100
def resizeEvent(self, event):
self.resized.emit()
#self.oldWidth = self.width()
#self.oldHeight = self.height()
self.oldButtonPosX = self.testButton.x()
self.oldButtonPosY = self.testButton.y()
if self.getOriginalDimensions:
self.oldWidth = self.width()
self.oldHeight = self.height()
return QtGui.QWidget.resizeEvent(self, event)
def someFunction(self):
if not self.getOriginalDimensions:
self.oldWidth = self.width()
self.oldHeight = self.height()
self.getOriginalDimensions = True
newWidth = self.width() - 20.0
newHeight = self.height() - 20.0
self.freeWidget.setGeometry(0,0,newWidth,newHeight)
#newButtonX = self.oldButtonPosX + ((self.width() - self.oldWidth))
yDifference = self.oldHeight - self.height()
print self.buttonYDifference
if self.buttonYDifference <= 3 and self.buttonYDifference >= -3:
if yDifference == 1:
self.buttonYDifference +=1
elif yDifference == -1:
self.buttonYDifference -=1
else:
newButtonY = self.oldButtonPosY - self.buttonYDifference
self.testButton.setGeometry(self.oldButtonPosX,newButtonY,20,20)
self.buttonYDifference = 0
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Related
Please consider the following code:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class Gallery(QScrollArea):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setFixedWidth(175)
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Expanding)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
# Set widget and layout
self._scroll_widget = QWidget()
self._layout = QVBoxLayout()
self._layout.setContentsMargins(0, 0, 0, 0)
self._layout.setSpacing(25)
self._scroll_widget.setLayout(self._layout)
self.setWidget(self._scroll_widget)
self.setWidgetResizable(True)
# Stretch
self._layout.addStretch(1) # Stretch above widgets
self._layout.addStretch(1) # Stretch below widgets
# Initialize ---------------------------------|
for _ in range(10):
self.add_item()
def resizeEvent(self, event: QResizeEvent) -> None:
super().resizeEvent(event)
# Calculate Margins --------------------|
children = self._scroll_widget.findChildren(QLabel)
first_widget = children[0]
last_widget = children[-1]
self._layout.setContentsMargins(
0,
int(event.size().height() / 2 - first_widget.size().height() / 2),
0,
int(event.size().height() / 2 - last_widget.size().height() / 2)
)
def add_item(self) -> None:
widget = QLabel()
widget.setStyleSheet('background: #22FF88')
widget.setFixedSize(90, 125)
child_count = len(
self._scroll_widget.findChildren(QLabel)
)
self._layout.insertWidget(1 + child_count, widget,
alignment=Qt.AlignCenter)
if __name__ == '__main__':
app = QApplication([])
window = Gallery()
window.show()
app.exec()
Currently, the margins of the layout are dynamically set, so that, no matter the size of the window, the first and last item are always vertically centered:
What I want to achieve now is that whenever I scroll (either by mousewheel or with the arrow-keys, as the scrollbars are disabled) the next widget should take the position in the vertical center, i.e. I want to switch the scrolling mode from a per pixel to a per-widget-basis, so that no matter how far I scroll, I will never land between two widgets.
How can this be done?
I found that QAbstractItemView provides the option the switch the ScrollMode to ScrollPerItem, though I'm not sure if that's what I need because I was a bit overwhelmed when trying to subclass QAbstractItemView.
Edit:
This shows the delay I noticed after adapting #musicamante's answer:
It's not really disrupting, but I don't see it in larger projects, so I suppose something is not working as it should.
Since most of the features QScrollArea provides are actually going to be ignored, subclassing from it doesn't give lots of benefits. On the contrary, it could make things much more complex.
Also, using a layout isn't very useful: the "container" of the widget is not constrained by the scroll area, and all features for size hinting and resizing are almost useless in this case.
A solution could be to just set all items as children of the "scroll area", that could be even a basic QWidget or QFrame, but for better styling support I chose to use a QAbstractScrollArea.
The trick is to compute the correct position of each child widget, based on its geometry. Note that I'm assuming all widgets have a fixed size, otherwise you might need to use their sizeHint, minimumSizeHint or minimum sizes, and check for their size policies.
Here's a possible implementation (I changed the item creation in order to correctly show the result):
from random import randrange
class Gallery(QAbstractScrollArea):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setFixedWidth(175)
self.items = []
self.currentIndex = -1
for _ in range(10):
widget = QLabel(str(len(self.items) + 1),
self, alignment=Qt.AlignCenter)
widget.setStyleSheet('background: #{:02x}{:02x}{:02x}'.format(
randrange(255), randrange(255), randrange(255)))
widget.setFixedSize(randrange(60, 100), randrange(50, 200))
self.addItem(widget)
def addItem(self, widget):
self.insertItem(len(self.items), widget)
def insertItem(self, index, widget):
widget.setParent(self.viewport())
widget.show()
self.items.insert(index, widget)
if len(self.items) == 1:
self.currentIndex = 0
self.updateGeometry()
def setCurrentIndex(self, index):
if not self.items:
self.currentIndex = -1
return
self.currentIndex = max(0, min(index, len(self.items) - 1))
self.updateGeometry()
def stepBy(self, step):
self.setCurrentIndex(self.currentIndex + step)
def updateGeometry(self):
super().updateGeometry()
if not self.items:
return
rects = []
y = 0
for i, widget in enumerate(self.items):
rect = widget.rect()
rect.moveTop(y)
rects.append(rect)
if i == self.currentIndex:
centerY = rect.center().y()
y = rect.bottom() + 25
centerY -= self.height() / 2
centerX = self.width() / 2
for widget, rect in zip(self.items, rects):
widget.setGeometry(rect.translated(centerX - rect.width() / 2, -centerY))
def sizeHint(self):
return QSize(175, 400)
def resizeEvent(self, event: QResizeEvent):
self.updateGeometry()
def wheelEvent(self, event):
if event.angleDelta().y() < 0:
self.stepBy(1)
else:
self.stepBy(-1)
I have QLineEdit in which I wanted to add a clear button at the end of it. I enabled clear button in QLineEdit, it was working fine. I need to add a custom clear button at the end of the QLineEdit, so I used addAction() of QLineEdit and added my custom icon. The problem is that I can't find a solution to increase the size, I tried increasing the image size and it's not working.
class TextBox(QFrame):
def __init__(self, parent):
super(TextBox, self).__init__(parent=parent)
self.setObjectName("textBox")
self.isActive = False
self.lineEdit = QLineEdit()
self.lineEdit.addAction(QIcon("assets/icons/clear#3x.png"), QLineEdit.TrailingPosition)
A QIcon does not have a specific size, as it's only "decided" by the widget that uses it. While most widgets that use icons have a iconSize property, the icons of actions in a QLineEdit are shown in a different way.
Up until Qt 5.11 (excluded), the size was hardcoded to 16 pixels if the line edit was smaller than 34 pixels or 32 if it was taller.
Starting from Qt 5.11 the size is retrieved using the style (through pixelMetric()), and this can be overridden using a proxy style:
class Proxy(QtWidgets.QProxyStyle):
def pixelMetric(self, metric, opt=None, widget=None):
if (metric == self.PM_SmallIconSize and
isinstance(widget, QtWidgets.QLineEdit)):
size = widget.property('iconSize')
if size is not None:
return size
return widget.fontMetrics().height()
return super().pixelMetric(metric, opt, widget)
class LineEdit(QtWidgets.QLineEdit):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setProperty('iconSize', 64)
# ...
For previous versions of Qt, though, things are a bit tricky. The only solution I came up with is to install event filters on all QToolButton that are children of the line edit (every action uses an internal QToolButton, including the clear action), manually set their geometry (required for correct click actions) and paint it in the event filter.
The following includes the proxystyle implementation in case the current version correctly supports it as explained before:
from PyQt5 import QtWidgets, QtCore, QtGui
if int(QtCore.QT_VERSION_STR.split('.')[1]) > 11:
IconSizeFix = False
else:
IconSizeFix = True
class Proxy(QtWidgets.QProxyStyle):
def pixelMetric(self, metric, opt=None, widget=None):
if (metric == self.PM_SmallIconSize and
isinstance(widget, QtWidgets.QLineEdit)):
size = widget.property('iconSize')
if size is not None:
return size
return widget.fontMetrics().height()
return super().pixelMetric(metric, opt, widget)
class LineEdit(QtWidgets.QLineEdit):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setProperty('iconSize', 64)
self.setClearButtonEnabled(True)
self.addAction(QtGui.QIcon("icon.png"), self.TrailingPosition)
font = self.font()
font.setPointSize(48)
self.setFont(font)
def checkButtons(self):
for button in self.findChildren(QtWidgets.QToolButton):
button.installEventFilter(self)
def actionEvent(self, event):
super().actionEvent(event)
if IconSizeFix:
self.checkButtons()
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.Paint:
if (source.defaultAction().objectName() == '_q_qlineeditclearaction' and
not self.text()):
return True
qp = QtGui.QPainter(source)
state = QtGui.QIcon.Disabled
if source.isEnabled():
state = QtGui.QIcon.Active if source.isDown() else QtGui.QIcon.Normal
iconSize = QtCore.QSize(*[self.property('iconSize')] * 2)
qp.drawPixmap(source.rect(), source.icon().pixmap(
self.windowHandle(), iconSize, state, QtGui.QIcon.Off))
return True
return super().eventFilter(source, event)
def resizeEvent(self, event):
if not IconSizeFix:
return
self.checkButtons()
buttons = self.findChildren(QtWidgets.QToolButton)
if not buttons:
return
left = []
right = []
center = self.rect().center().x()
for button in buttons:
geo = button.geometry()
if geo.center().x() < center:
left.append(button)
else:
right.append(button)
left.sort(key=lambda x: x.geometry().x())
right.sort(key=lambda x: x.geometry().x())
iconSize = self.property('iconSize')
margin = iconSize / 4
top = (self.height() - iconSize) / 2
leftMargin = rightMargin = 0
if left:
x = margin
leftEdge = left[-1].geometry().right()
for button in left:
geo = QtCore.QRect(x, top, iconSize, iconSize)
button.setGeometry(geo)
x += iconSize + margin
leftMargin = x - leftEdge - margin
if right:
rightEdge = self.width() - margin
x = rightEdge - len(right) * iconSize - (len(right) - 1) * margin
rightMargin = self.width() - rightEdge + margin
for button in right:
geo = QtCore.QRect(x, top, iconSize, iconSize)
button.setGeometry(geo)
x += iconSize + margin
self.setTextMargins(leftMargin, 0, rightMargin, 0)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle(Proxy())
w = LineEdit()
w.show()
sys.exit(app.exec_())
Consider the following:
using the pre-5.11 workaround the positioning is not pixel-perfect, I tried to mimic what QLineEdit does to keep the code as simple as possible;
the painting is not exactly the same, most importantly the icon is missing the "highlight" shade when clicked, and if the style uses fade in/out effects for the clear button those effects won't be available;
the QProxyStyle method also affects the sizeHint of the QLineEdit, so it can not be smaller than the icon size, so use it with care;
Similar to this question, but for pyqt. I have an application that has two threads, one of which processes some data (time consuming), and the second thread that presents the results and asks for verification on the results. I want to show the number of objects processed in a progress bar. However, I also want to show the number of objects verified by user. Number processed will always be equal or greater than the number of objects verified (since you can't verify what hasn't been verified). In essence, it's kind of like the loading bar of a youtube video or something, showing a grey part that is "loaded" and red part that is "watched." Is this something that can be supported in pyqt? The documentation for QProgressBar does not seem to hint that there's any support. Using PyQt5 and Python 3.6.
It should look similar to this:
Here's a minimal viable code that has TWO separate progress bars, one for the number of objects processed and the other for the number verified, but I want them overlapped...
import sys
from PyQt5.QtWidgets import (QApplication, QDialog,
QProgressBar, QPushButton)
class Actions(QDialog):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('Progress Bar')
self.objectsToProcess = 100
self.objectsProcessed = 0
self.objectsVerified = 0
self.processProgress = QProgressBar(self)
self.processProgress.setGeometry(5, 5, 300, 25)
self.processProgress.setMaximum(self.objectsToProcess)
self.verifyProgress = QProgressBar(self)
self.verifyProgress.setGeometry(5, 35, 300, 25)
self.verifyProgress.setMaximum(self.objectsToProcess)
self.processButton = QPushButton('Process', self)
self.processButton.move(5, 75)
self.verifyButton = QPushButton('Verify', self)
self.verifyButton.move(90, 75)
self.show()
self.processButton.clicked.connect(self.process)
self.verifyButton.clicked.connect(self.verify)
def process(self):
if self.objectsProcessed + 1 < self.objectsToProcess:
self.objectsProcessed += 1
self.processProgress.setValue(self.objectsProcessed)
def verify(self):
if self.objectsVerified < self.objectsProcessed:
self.objectsVerified += 1
self.verifyProgress.setValue(self.objectsVerified)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Actions()
sys.exit(app.exec_())
Result from above code:
A possible solution is to create a new attribute in QProgressBar that shows the alternative advance, and to do the painting we can use a QProxyStyle:
from PyQt5 import QtCore, QtGui, QtWidgets
class ProxyStyle(QtWidgets.QProxyStyle):
def drawControl(self, element, option, painter, widget):
if element == QtWidgets.QStyle.CE_ProgressBar:
super(ProxyStyle, self).drawControl(element, option, painter, widget)
if hasattr(option, 'alternative'):
alternative = option.alternative
last_value = option.progress
last_pal = option.palette
last_rect = option.rect
option.progress = alternative
pal = QtGui.QPalette()
# alternative color
pal.setColor(QtGui.QPalette.Highlight, QtCore.Qt.red)
option.palette = pal
option.rect = self.subElementRect(QtWidgets.QStyle.SE_ProgressBarContents, option, widget)
self.proxy().drawControl(QtWidgets.QStyle.CE_ProgressBarContents, option, painter, widget)
option.progress = last_value
option.palette = last_pal
option.rect = last_rect
return
super(ProxyStyle, self).drawControl(element, option, painter, widget)
class ProgressBar(QtWidgets.QProgressBar):
def paintEvent(self, event):
painter = QtWidgets.QStylePainter(self)
opt = QtWidgets.QStyleOptionProgressBar()
if hasattr(self, 'alternative'):
opt.alternative = self.alternative()
self.initStyleOption(opt)
painter.drawControl(QtWidgets.QStyle.CE_ProgressBar, opt)
#QtCore.pyqtSlot(int)
def setAlternative(self, value):
self._alternative = value
self.update()
def alternative(self):
if not hasattr(self, '_alternative'):
self._alternative = 0
return self._alternative
class Actions(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('Progress Bar')
self.objectsToProcess = 100
self.objectsProcessed = 0
self.objectsVerified = 0
self.progress_bar = ProgressBar(maximum=self.objectsToProcess)
self.process_btn = QtWidgets.QPushButton('Process')
self.verify_btn = QtWidgets.QPushButton('Verify')
self.process_btn.clicked.connect(self.process)
self.verify_btn.clicked.connect(self.verify)
lay = QtWidgets.QGridLayout(self)
lay.addWidget(self.progress_bar, 0, 0, 1, 2)
lay.addWidget(self.process_btn, 1, 0)
lay.addWidget(self.verify_btn, 1, 1)
def process(self):
if self.objectsProcessed + 1 < self.objectsToProcess:
self.objectsProcessed += 1
self.progress_bar.setValue(self.objectsProcessed)
def verify(self):
if self.objectsVerified < self.objectsProcessed:
self.objectsVerified += 1
self.progress_bar.setAlternative(self.objectsVerified)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle(ProxyStyle(app.style()))
w = Actions()
w.show()
sys.exit(app.exec_())
Thanks for #eyllanesc for providing a robust solution. I chose to go with a lighter (admittedly hackish) solution of overlapping two progress bars and making the top bar slightly transparent using QGraphicsOpacityEffect.
# Opaque prog bar
self.verifyProgress = QProgressBar(self)
self.verifyProgress.setGeometry(5, 5, 300, 25)
self.verifyProgress.setMaximum(self.objectsToProcess)
self.verifyProgress.setFormat('%p% / ')
self.verifyProgress.setAlignment(Qt.AlignCenter)
# Must set the transparent prog bar second to overlay on top of opaque prog bar
self.processProgress = QProgressBar(self)
self.processProgress.setGeometry(5, 5, 300, 25)
self.processProgress.setMaximum(self.objectsToProcess)
self.processProgress.setFormat(' %p%')
self.processProgress.setAlignment(Qt.AlignCenter)
op = QGraphicsOpacityEffect(self.processProgress)
op.setOpacity(0.5)
self.processProgress.setGraphicsEffect(op)
Result:
I needed to do something similar recently and choose to use a gradient color for the progressbar chunk since I needed to use stylesheets too.
def set_pb_value(self, pb, value_1, value_2):
if value_2 > value_1:
pb.setValue(value_2)
pb.setFormat("{} / {}".format(value_1, value_2))
pb.setStyleSheet('QProgressBar::chunk {' +
'background-color: qlineargradient(spread:pad, x1:' + str(value_1/pb.maximum()) + ', y1:0, x2:' +
str(value_1/value_2) + ', y2:0, stop:' + str(value_1/value_2) + ' rgba(0, 255, 0, 255), stop:1 '
'rgba(255, 0, 0, 255)); width: -1px; margin: -1px;}')
else:
pb.setValue(value_1)
pb.setFormat("%v")
My values were whole numbers so value_1/pb.maximum() was necessary for me, but change it per your needs.
I also had some issues with the other stylesheets and the progressbar margins which is why they are set to -1 right now, you may not need to include that.
I have an widget that contain a lot of children , i want to set its background transparent , but keep its children opaque.
I'am using PySide 1.2.1 win7
I have try something like this:
self.setAttribute(Qt.WA_TranslucentBackground,True);
self.setStyleSheet("background-color:rgba(40,40,150,150);border:0px;")
but it dosen't work for me . It make the whole widget transparent . I want to keep the children opaque. Can someone help me ? Thank you so much. Here is my code , thanks for help.
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class M_SCROLL_GRID_WIDGET(QWidget):
# This is a common ui class .
def __init__(self,column_count):
super(M_SCROLL_GRID_WIDGET, self).__init__()
self.column_count = column_count
self.visible_widget_list = []
self.scroll_widget = QWidget()
self.scroll_area = QScrollArea()
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setWidget(self.scroll_widget)
self.main_layout = QGridLayout()
self.main_layout.setSpacing(0)
self.scroll_widget.setLayout(self.main_layout)
self.main_layout.setAlignment(Qt.AlignTop)
master_layout = QVBoxLayout(self)
master_layout.addWidget(self.scroll_area)
self.setLayout(master_layout)
self.setAttribute(Qt.WA_TranslucentBackground, True)
self.setStyleSheet("background-color:rgba(40,40,150,150);border:0px;")
def Get_Widget_Count(self):
return len(self.visible_widget_list)
def Add_Widget_To_Vis_List(self, widget):
self.visible_widget_list.append(widget)
def Remove_Widget_To_Vis_List(self, widget):
self.visible_widget_list.remove(widget)
def Remove_All_Widgets(self):
for i in reversed(range(self.main_layout.count())):
widget = self.main_layout.itemAt(i).widget()
widget.setParent(None)
def Clean_Visible_List(self):
self.visible_widget_list = []
def Refresh(self):
for widget_index in xrange(len(self.visible_widget_list)):
r_position = widget_index / self.column_count
c_position = (widget_index + 1) % self.column_count
if c_position == 0:
c_position = self.column_count
self.main_layout.addWidget(self.visible_widget_list[widget_index], r_position, c_position)
if __name__ == '__main__':
app = QApplication(sys.argv)
caofan = M_SCROLL_GRID_WIDGET(3)
btn_list = []
caofan.Remove_All_Widgets()
for a in xrange(50):
btn = QPushButton(str(a+1))
btn_list.append(btn)
caofan.Add_Widget_To_Vis_List(btn)
caofan.Refresh()
caofan.show()
app.exec_()
I suspect the real problem is that the style sheet you specify is propagating to the child widgets.
So, remove...
self.setStyleSheet("background-color:rgba(40,40,150,150);border:0px;")
and override QWidget::paintEvent with something like...
def paintEvent(self, event):
painter = QPainter(self)
painter.fillRect(self.rect(), QColor(40, 40, 150, 150))
This code runs a little window with a toolbar and a QTextEdit area.
If you highlight 'bananas' and change the font size, then using zoom from either the toolbar buttons or CTRL + mouse wheel will only resize 'apples'. Anyone know why?
from PySide import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.textEdit = Editor(self)
self.toolBar = QtGui.QToolBar(self)
self.addToolBar(self.toolBar)
self.setCentralWidget(self.textEdit)
self.textEdit.setHtml('<font color=blue>apples bananas</font>')
# Zoom
self.actionZoomIn = QtGui.QAction('Zoom In', self)
self.actionZoomOut = QtGui.QAction('Zoom Out', self)
self.toolBar.addAction(self.actionZoomIn)
self.toolBar.addAction(self.actionZoomOut)
self.actionZoomIn.triggered.connect(self.onZoomInClicked)
self.actionZoomOut.triggered.connect(self.onZoomOutClicked)
# Font Size
self.comboSize = QtGui.QComboBox(self.toolBar)
self.toolBar.addWidget(self.comboSize)
self.comboSize.addItem('0')
self.comboSize.addItem('10')
self.comboSize.addItem('18')
self.comboSize.addItem('30')
self.comboSize.addItem('48')
self.comboSize.setCurrentIndex(1)
self.comboSize.activated[str].connect(self.textSize)
def textSize(self, pointSize):
pointSize = int(pointSize)
if pointSize > 0:
fmt = QtGui.QTextCharFormat()
fmt.setFontPointSize(pointSize)
self.mergeFormatOnWordOrSelection(fmt)
def mergeFormatOnWordOrSelection(self, format):
cursor = self.textEdit.textCursor()
if not cursor.hasSelection():
cursor.select(QtGui.QTextCursor.WordUnderCursor)
cursor.mergeCharFormat(format)
self.textEdit.mergeCurrentCharFormat(format)
def onZoomInClicked(self):
self.textEdit.zoom(+1)
def onZoomOutClicked(self):
self.textEdit.zoom(-1)
class Editor(QtGui.QTextEdit):
def __init__(self, parent=None):
super(Editor, self).__init__(parent)
self.zoomValue = 0
def zoom(self, delta):
zoomIncrement = 3
if delta < 0:
zoomIncrement = 0 - zoomIncrement
self.zoomIn(zoomIncrement)
self.zoomValue = self.zoomValue + zoomIncrement
print "self.zoomValue", self.zoomValue
def wheelEvent(self, event):
if (event.modifiers() & QtCore.Qt.ControlModifier):
self.zoom(event.delta())
if __name__ == '__main__':
app = QtGui.QApplication([])
window = MainWindow()
window.resize(400, 180)
window.show()
app.exec_()
The default implementation of QTextEdit.zoomIn/Out simply changes the pointSize of the base font for the text-edit.
The method used in the example to change font size wraps the selected word in a span tag and uses inline css to set the font-size property to a fixed value. This means that when the text-edit is subsequently zoomed, only the unchanged text will be affected.
It would be possible overcome this problem by using relative font sizes. However, it looks like only a limted subset of css properties is supported, so its only possible to set imprecise values like small, large, etc.
This can be implemented in the example by making the following changes:
# Font Size
self.comboSize = QtGui.QComboBox(self.toolBar)
self.toolBar.addWidget(self.comboSize)
self.comboSize.addItem('small')
self.comboSize.addItem('medium')
self.comboSize.addItem('large')
self.comboSize.addItem('x-large')
self.comboSize.addItem('xx-large')
self.comboSize.setCurrentIndex(1)
self.comboSize.activated[int].connect(self.textSize)
def textSize(self, size):
fmt = QtGui.QTextCharFormat()
fmt.setProperty(QtGui.QTextFormat.FontSizeAdjustment, size - 1)
self.mergeFormatOnWordOrSelection(fmt)