Qt Designer make widget overlap another widget in layout - pyqt

I want to make 3 window button like above picture (similar Google Chrome) use Qt Designer and PyQt.
I want 3 buttons overlap right side of TabWidget.
But I only can overlap the Button on TabWidget when break layout like the picture.
When I set any layout, every widget can not overlap on each other.
So can I overlap when set layout? Thanks.
This is the layout I want
It similar Google Chrome's layout

This cannot be done in creator/designer, and can only be achieved using setCornerWidget() from your code.
Since only one widget can be set for each corner, you have to create a QWidget that acts as a container, then add the buttons to it.
class Test(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# ...
self.createButtons()
def createButtons(self):
# create the container and its layout
self.buttonContainer = QtWidgets.QWidget()
buttonLayout = QtWidgets.QHBoxLayout(self.buttonContainer)
# remove margins around the layout and set a minimal spacing between
# the children widgets
buttonLayout.setContentsMargins(0, 0, 0, 0)
buttonLayout.setSpacing(1)
# QToolButtons are usually better for this, as QPushButtons tend
# to expand themselves
self.minimizeButton = QtWidgets.QToolButton(text='_')
self.maximizeButton = QtWidgets.QToolButton(text='o')
self.closeButton = QtWidgets.QToolButton(text='x')
buttonLayout.addWidget(self.minimizeButton)
buttonLayout.addWidget(self.maximizeButton)
buttonLayout.addWidget(self.closeButton)
# set the container as the corner widget; as the docs explain,
# despite using "TopRightCorner", only the horizontal element (right
# in this case) will be used
self.tabWidget.setCornerWidget(
self.buttonContainer, QtCore.Qt.TopRightCorner)

Related

How to get the color of a widget text, and set it as the color of another widget

I have two widgets
first, a QLineEdit that has a stylesheet (assume not accessible or changeable) that sets the color of the text to some color (e.g., red).
second, QLabel that has a default text color (e.g., black)
How can I get the color of the text in the QLineEdit, and set it as the color in the QLabel
l1 = QLineEdit()
color_to_be_set= l1.palette().highlight().color().name() # this approach is giving the wrong color
q1 = QLabel()
q1.setText("the text")
# then set the new color
"Trial#1 (Not working at all, not changing the color)"
palette = q1.palette()
palette.setColor(QPalette.WindowText,QColor(color_to_be_set))
q1.setPalette.....
"Trial#2, gives the wrong color"
q1.setStyleSheet("color:{}".format(color_to_be_set))
As already explained in comments, there's no public API to access properties set by stylesheets, and I sincerely doubt there could be any (at least, with the current implementation, including Qt6).
That said, there is some level of access to those properties, but it only works for the following:
background and background-color (which are the same);
color;
selection-background-color;
selection-color;
alternate-background-color;
Whenever any of the properties above is set, the palette of the widget is altered with the following palette color roles:
QSS property
QPalette role(s)
background, background-color
Base, Button, Window, backgroundRole()*
color
Text, ButtonText, WindowText, foregroundRole()*
selection-background-color
Highlight
selection-color
HighlightedText
alternate-background-color
AlternateBase
The * values are references to the widget class definitions (for instance, QPushButton normally uses Button as foreground role). They may be overridden by their respective setter functions.
Also, those properties only support the QSS Brush types: plain colors, gradients or palette(<role>). No pixmaps here!
Remember that in order to get proper access to those properties, the widget must be polished, which means that, if the widget has not been ever shown yet, calling ensurePolished() is mandatory:
Ensures that the widget and its children have been polished by QStyle (i.e., have a proper font and palette).
This means that any new Qt widget created without any already polished parent will use the system palette.
Consider the following:
from PyQt5.QtWidgets import *
app = QApplication([])
app.setStyleSheet('* {color: green;}')
source = QWidget()
# source.ensurePolished() # leave this commented for now
color = source.palette().windowText().color()
target = QWidget(styleSheet='background: {}'.format(color.name()))
target.show()
app.exec()
Then uncomment the fifth line, and see the result.
Now, there are a few catches.
When a stylesheet is set, any widget affected by it gets it's style() overridden by a private QStyleSheetStyle. That style is not exposed by the API and completely takes control over the style set for each affected widget.
But not always.
Take this snippet:
from PyQt5.QtWidgets import *
app = QApplication([])
app.setStyleSheet('* {background-color: black;}')
topLevel = QTextEdit()
topLevel.show()
parent = QWidget()
layout = QVBoxLayout(parent)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(QTextEdit())
parent.show()
app.exec()
With my current style (oxygen), the two backgrounds are slightly different:
On the left there's the topLevel widget, on the right the parent. As you can see, the underlying style still has some level of control.
This is an important aspect that has to be kept in mind, and trying to override it with a QProxyStyle won't be enough: the QStyleSheetStyle will "take control" and completely ignore the overridden functions, except for a very few like drawItemText(). Any other function will be called on "the C++ side" implementation, for which there's no useful override in the python bindings.
Also, whenever the underlying style is still used internally by the QSS, the result is that the color properties will be used just like any QPalette: they are reference colors, not the actual ones. Any widget will use the style implementation, which might use different color roles, or completely alter the color depending on the situation.
Whenever the default style is used (even through QStyleSheetStyle modifications), the palette is just a reference, not unlike a physical palette: give the same basic color palette to two styled painters, and they will use its colors in different ways. The actual color shown to the user might be slightly or even completely different and possibly ignored.
Concluding, the following aspects are of utmost importance:
just created widgets will use the default palette;
there is no access to border properties, nor margins and paddings (with the last two also depending on the inherited style);
top level widgets might behave differently;
the style might not completely adhere to the colors set (and even ignore them);
palette colors are references: the style might use a role for an unexpected purpose, or completely ignore it; some common styles use the Button role for item view headers, other use the Window role;
while normally respected, the above color properties might be altered by the style, just like any palette;
basic QSS color properties can be properly accessed only after the widget has been polished: ensurePolished() has to be called, which normally happens after the first visibility change and any setStyleSheet() call that affects the widget (see QStyle.unpolish() and QStyle.polish());
QPalette uses QBrush for any of its roles and groups, which could be any of the following: a QColor, a QGradient or a QPixmap; this means that if a palette role uses a gradient or pixmap, the returned color() will be invalid, etc.;
background-image does not update the palette (in fact, it's not listed in the table above); the same goes for border-image (as said above, there is no access to border properties);
the fusion style is (usually) the most reliable style to be used for extended stylesheet implementation; when unsure or getting unexpected behavior, use QApplication.setStyle('fusion') or widget.setStyle(QStyleFactory.create('fusion'));
OS styles potentially override drawing with private pixmaps in order to respect the standard OS appearance;
complex widgets require full and explicit values for their properties; never set generic properties (aka, no selectors) for parent widgets or the application[1], including QComboBox, QScrollBar and all QAbstractScrollArea subclasses (including item views);
So, considering all this, as long as a standard QStyle is used and the widget is polished, the roles explained in the above table should return the colors set in the supported properties of any stylesheet, including inherited ones.
But you can never be sure about that, and you always have to be aware of it.
[1] in the code examples above I did use generic stylesheets, but that was on purpose and for simplicity; that practice is usually highly discouraged for normal usage. Always use selectors when setting stylesheets for parents and complex widgets (see above).

Cannot position button content neither with anchor nor with justify

I have dynamic button label, which changes everytime you click it. The problem is, when text on the button changes, the same goes with button width. For this reason I set fixed width for my button.
After that, another problem appeared. Content is centered within button, so on every change image and label move a bit. For this reason I tried to position everything to the left side to reduce content trembling.
I looked through docs and found that button widget supports anchor option to position content. However, when I try it, I'm getting error:
_tkinter.TclError: unknown option "-anchor"
I found another option, justify, but result is the same:
_tkinter.TclError: unknown option "-justify"
Anyone knows how to deal with it? I'll share my code with you:
self.btnServiceIo = ttk.Button(
top_buttons_frame,
text="Usługa automatyczna",
image=self.ioBtnImg_off,
compound=tk.LEFT,
width=30,
justify=tk.LEFT,
anchor='w')
self.btnServiceIo.pack(side=tk.LEFT)
self.btnServiceIo.bind("<ButtonRelease>", self.IObuttonToggle)
(I don't use justify and anchor at the same time, I just posted them together to show you the way I'm using it)
I'll post the picture of my app for your attention. My client would like to have not only toggle image which states the status of some service, but also text information nearby.
For ttk buttons you can set the anchor option in the ttk style, see the anchor entry on this page.
So for example:
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
root = tk.Tk()
s = ttk.Style()
s.configure('TButton', anchor=tk.W)
buttonImage = Image.open('test.png')
buttonPhoto = ImageTk.PhotoImage(buttonImage)
btnServiceIo = ttk.Button(
root,
text="Usługa automatyczna",
image=buttonPhoto,
compound=tk.LEFT,
width=30)
btnServiceIo.pack(side=tk.LEFT)
root.mainloop()
Answer from #fhdrsdg was almost perfect, but it messed with some of my simple buttons with text only, centered in the button. I had to make dedicated style for my button as following:
# Access ttk style object
s = ttk.Style()
# Set dedicated style for button, derived from main button class
s.configure('service-io.TButton', anchor=tk.W)
self.ioBtnImg_off = tk.PhotoImage(file='../img/io.png')
self.ioBtnImg_on = tk.PhotoImage(file='../img/io_on.png')
self.btnServiceIo = ttk.Button(
top_buttons_frame,
text="Usługa automatyczna",
image=self.ioBtnImg_off,
compound=tk.LEFT,
width=30,
style='service-io.TButton' # use dedicated style
)

How can I detect a mouse click on a custom wx.Panel?

I am pretty new at both Python and wxPython. Anyway, after having followed the official tutorial where they explain how to do a basic text editor, I decided to go ahead and write a real text editor.
Now, my text editor consists of a MainWindow (which inherits from wx.Frame), which in turn contains a Notebook (inheriting from wx.Notebook) which in turn contains several tabs (a custom class inheriting from wx.Panel).
If I didn't misunderstand, events in wxPython can be detected and bounded to specific objects via the Bind() function.
Here's my custom panel class:
class TabContent(wx.Panel) :
def __init__(self, parent) :
# Calls the constructor for wx.Panel
wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
# Creates a vertical sizer
sizer = wx.BoxSizer(wx.VERTICAL)
# Creates an empty multiline wx.TextCtrl
textArea = wx.TextCtrl(self, style = wx.TE_MULTILINE)
# Adds the text area to the sizer
sizer.Add(textArea, 1, wx.EXPAND | wx.ALL, 2)
# Sets the previously created sizer as this panel's sizer
self.SetSizer(sizer)
# Sets up events
self.Bind(wx.EVT_RIGHT_DOWN, self.onMouseLeftClicked)
def onMouseLeftClicked(self, event) :
print("Left button of the mouse was clicked\n")
I'd like to be able to detect right clicks on the tab itself (for example I could open a menu or just print something for the sake of testing wxPython functions). However, clicking with the mouse does not print anything. Any idea why?
By the way, I am on ArchLinux, using PyCharm Community Edition 2016.2.3, Python 3.5.2 and wxpython 3.0.2.
The event was actually being caught, but only on the very thin border of the tab.
Solved by putting the event handler in the Notebook class.

PyQt adding a scrollbar to my main window

I want to add a scrollbar to my main window
Here's a simplified version of my code since mine is too long.
class Main(QtGui.QWidget):
def __init__(self):
super(Main, self).__init__()
verticalLayout = QtGui.QVBoxLayout()
self.setGeometry(0,0,400,400)
self.setLayout(verticalLayout)
label_1 = QtGui.QLabel("label 1")
self.verticalLayout.addWidget(label_1)
...(many more that exceed my computer screen)
so I've been reading a lot of posts about scrollbars but I'm still not quite sure as to how to implement it.
Within your main window, you need to create a Scroll Area.
http://pyqt.sourceforge.net/Docs/PyQt4/qscrollarea.html

QScrollArea widgets and layouts - PySide/PyQt

I've read endless examples on nullege, as well as previous questions on this topic on SO. I'm thoroughly confused with trying to set up a QScrollArea within my PySide UI project. Here's what I'm trying to achieve:
A QWidget or QFrame which acts as a container on a page.
The QWidget/QFrame contains 8 Pixmapped labels.
Only 2 of the Pixmapped labels can 'fit' within the visible part of the screen at a time.
A QScrollArea is utilised such that the entire range of the QWidget/QFrame can be seen, using the horizontal scroll bars.
In theory this is really quite simple, but I just can't figure out how the QScrollArea links with the QWidget/QFrame, and whether I need a QLayout as well. At the moment, the horizontal scroll bars are present, but scrolling is unavailable. Here's what I have so far:
#Oscar Home Scroll Area - Scroll Area#
self.ScrollAreaOscarHome = QtGui.QScrollArea(self)
self.ScrollAreaOscarHome.setGeometry(QtCore.QRect(12, 180, 1000, 570))
self.ScrollAreaOscarHome.setVisible(True)
self.ScrollAreaOscarHome.setFrameShape(QtGui.QFrame.NoFrame)
self.ScrollAreaOscarHome.setObjectName("ScrollAreaOscarHome")
self.ScrollAreaOscarHome.setWidgetResizable(True)
self.ScrollAreaOscarHome.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.ScrollAreaOscarHome.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
#Oscar Home Container Widget - Widget#
self.WidgetOscarHomeContainer = QtGui.QWidget()
self.WidgetOscarHomeContainer.setGeometry(QtCore.QRect(12, 180, 2000, 570))
self.WidgetOscarHomeContainer.setObjectName("WidgetOscarHomeContainer")
self.ScrollAreaOscarHome.setWidget(self.WidgetOscarHomeContainer)
#Oscar Home Horizontal Layout - QHBoxLayout#
self.OscarHomeHorizontalLayout = QtGui.QHBoxLayout(self.WidgetOscarHomeContainer)
I've included the QHBoxLayout section above because previous examples I've read used the Layout, but I'm not sure how it fits in to the Widget and the ScrollArea.
Am I on the right track here? I'm not getting any errors, and the ScrollArea is displaying as I'd like, but the ScrollArea and Widget (which is much wider than the ScrollArea) don't seem to be linked at all.
Any guidance would be greatly appreciated!

Resources