Applying setStyle to QLabel in PySide/PyQt - python

In PySide, I am trying to change the overall style of a simple GUI window (a QLabel object), as outlined at the PySide documentation:
http://srinikom.github.io/pyside-docs/PySide/QtGui/QStyle.html#detailed-description
That is, as described at that link, I want to use setStyle to change the overall GUI format to one of the pre-defined GUI styles (e.g., from Windows to Cleanlooks). (As I note below, this goal is different from tweaking colors and such using setStyleSheet, which I already know how to do).
To get started, I first checked what style types I have available:
print QtGui.QStyleFactory.keys()
This revealed many available styles, including 'Cleanlooks'.
Second, based on documentation from three sources (PySide, ZetCode, and Nullege), I tried to change my QLabel style to 'Cleanlooks':
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Cleanlooks'))
Unfortunately, this changed nothing. In fact, nothing I enter has changed a single pixel in my Hello World widget. Not sure if relevant, but when I print the output of the previous line of code, I always get None. Full example code is below the fold.
I am in Windows 7, Python 2.7 running within iPython. Based on replies, this seems to be an issue with PyQt4 in Windows 7 too, not just PySide.
Just to be extra clear, I am not asking about how to use setStyleSheets to tweak my widget. This has been addressed decently already:
Styling with classes in Pyside + Python
Example of code I tried (other permutations I tried are shown in comments):
# coding: utf-8
import sys
from PySide import QtGui
class HelloWorldApp(QtGui.QLabel):
def __init__(self):
QtGui.QLabel.__init__(self, "Hello, world!")
self.initUI()
def initUI(self):
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Cleanlooks'))
self.show()
def main():
#QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Cleanlooks')) #crashes program
qt_app=QtGui.QApplication(sys.argv)
#QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Cleanlooks')) #does nothing
exHell=HelloWorldApp()
sys.exit(qt_app.exec_())
if __name__=="__main__":
main()

The most helpful resource I found, which answered my question, was this wonderful gallery of widgets:
Widget Gallery
It shows the look of pretty much every type of widget under the different styles.
It turns out my "problem" with QLabel was not a bug, but rather that a bare QLabel is so simple that changing its style rarely influences how it looks. That is, when it is the only thing you have in your display, at best it will not be obvious when you change the style (as in my Hello World example in my question).
As expected, when I replaced the simple QLabel in my example with a combobox or other more complex widget, the style differences showed very clearly, just as described in the Widget Gallery.
Note the above Widget Gallery was pointed out when I asked about this at Qt Centre

Related

Why are my QSplitter's QSplitterHandles refusing to get formatted?

I have a simple QSplitter with a few widgets thrown in:
...
actions_splitter = QSplitter(
Qt.Horizontal,
frameShape=QFrame.StyledPanel,
frameShadow=QFrame.Plain
)
actions_splitter.addWidget(self.systems)
actions_splitter.addWidget(self.events)
actions_splitter.addWidget(self.compass_widget)
actions_splitter.setChildrenCollapsible(False)
...
The resulting splitters function perfectly fine, in that I can hover my mouse over their position, see the cursor change, and drag them around and change the sizes of the neighboring widgets.
The problem is that the handles themselves, the QSplitterHandles, absolutely refuse to have any visual modifications stick to them.
I have tried style sheets:
handle = actions_splitter.handle(0)
handle.setStyleSheet("{background-color: red}")
I have tried adding layouts with widgets to them:
handle = actions_splitter.handle(0)
layout = QHBoxLayout()
widget = QPushButton("HANDLE")
layout.addWidget(widget)
handle.setLayout(layout)
And absolutely nothing is doing anything to change how these handles appear. The only "visual" thing I can change is their width, and that's via QSplitter.setHandleWidth().
Someone please point out the painfully, stupidly obvious thing I'm missing here so I can get on with my life.
Premise
QSplitterHandle is one of the few very specific widgets that don't seem to follow generic QWidget "rules"; the reason is pretty simple: its aim is normally limited to the usage it's intended for, which is being a QSplitter child.
Then, you have to consider that, while pretty "liberal", the stylesheet syntax follows basic but important rules, it depends on the underlying QStyle in use and its behavior might change whenever a child widget is very dependant on its parent, as in this case.
First of all, there's a conceptual problem in your approach: the stylesheet syntax is wrong, as you're trying to use a selector-like syntax (using {braces}) without specifying a selector. With a syntax like that, the stylesheet won't be applied anyway, even with much more "standard" widgets.
But that's not enough. The QSplitter handle() documentation explains an important point (emphasis mine):
Returns the handle to the left of (or above) the item in the splitter's layout at the given index, or nullptr if there is no such item. The handle at index 0 is always hidden.
Even if your syntax were correct, it wouldn't have worked anyway since you tried to apply the stylesheet to the first (hidden) handle.
So, theoretically, the correct syntax should've been the following:
self.splitter.handle(1).setStyleSheet("background-color: red;")
even better:
self.splitter.handle(1).setStyleSheet(
"QSplitterHandle {background-color: red;}")
or, for all handles:
self.splitter.setStyleSheet(
"QSplitterHandle {background-color: red;}")
On the other hand, since QSplitterHandle is a very peculiar widget, its color properties are applied in different ways according to the currently applied QStyle, even when a correct syntax is used. Remember that stylesheets are overrides to the current QStyle, which means that the QStyles states the painting rules, and applies the eventual stylesheet according to those rules.
For instance, while the "windows" style uses the background color rule of the stylesheet to draw the actual background of the handle, the fusion style uses the background color to draw the "dots" of the splitter only.
As with other complex widgets (like QComboBox or QAbstractItemView), you need to use specific selectors for sub-controls, as clearly explained in the QSplitter stylesheet documentation; using the selector you ensure that the color rule is applied to the handle as a whole, no matter the style in use:
self.splitter.setStyleSheet("QSplitter::handle {background-color: red}")

Changing background color (bgc) of windows in Maya?

I worked somewhere in the past where since we had multiple sessions of Maya open, the background color could be randomly changed so when you switched from a session quickly it was easy to sort out which window belonged to what Maya session.
And so far, I can change the bgc of the main UI by using:
window -e bgc 0.5 0.5 0.5 $gMainWindow;
After searching for other global variables, I found $AllWindows, $CommandWindow, among others since the docs state that 'bgc' is a windows only flag. I can not get any of the colors to change on any window besides the $gCommandWindow, which popped up and I do not recall seeing it before.
I'm hoping to at least change the Script Editor window in addition to MainWindow if anyone knows whether it's possible or not? It is not mission critical, but now I'm interested in seeing if it can be done.
Thanks!
Since Maya's interface is using Qt, you can use the power of PySide to tweak any widget you want. Usually the only tricky part is actually finding the proper widget to modify.
Here's how you can tweak the Script Editor to give it a yellow border:
import shiboken2
from maya import cmds
from maya import OpenMayaUI
from PySide2 import QtWidgets
panels = cmds.getPanel(scriptType="scriptEditorPanel") # Get all script editor panel names.
if panels: # Make sure one actually exists!
script_editor_ptr = OpenMayaUI.MQtUtil.findControl(panels[0]) # Grab its pointer with its internal name.
script_editor = shiboken2.wrapInstance(long(script_editor_ptr), QtWidgets.QWidget) # Convert pointer to a QtWidgets.QWidget
editor_win = script_editor.parent().parent().parent().parent() # Not very pretty but found that this was the best object to color with. Needed to traverse up its parents.
editor_win.setObjectName("scriptEditorFramePanel") # This object originally had no internal name, so let's set one.
editor_win.setStyleSheet("#scriptEditorFramePanel {border: 3px solid rgb(150, 150, 45);}") # Set its styleSheet with its internal name so that it doesn't effect any of its children.
OpenMayaUI.MQtUtil gives you the awesome ability to find any control by name, so as long as you know the name of the widget you want to modify, you can find it (tough part is finding it sometimes!). In this case I had to traverse up a few parents to find one that worked best to outline the whole window. You can fool around with this and color, let's say, only the text area. And since this is PySide's style sheets you can do whatever your heart desires, like effect the background color, the thickness of the outline, and so on.
Since we're only effecting the style sheet this also won't save with the preferences and will revert to what it was on a new session.
Hope that helps.

How to remove icon from QMessageBox in PyQt5?

I am trying to code a message box that just pops up and says something and has a single button to close it however as I only have a small amount of text in the box the icon/image to the left of it is huge and looks bad. I want to know how to remove it. I am also having trouble making custom message boxes. Tutorials say that this is how you make a custom message box:
box = QMessageBox()
box.setText('text')
box.addButton(QPushButton('Close', self))
box.exec_()
However this just closes my program and returns a 1. My current code uses the about method of QMessageBox():
box = QMessageBox().about(self, 'About', 'This is a test Program')
However this has that large icon in the text window and I can't seem to do anything else to the box as it just stops the program and returns 1 again
I am in desperate need of some decent PyQt documentation. I can't seem to find documentation on much at all unless it is in C++. For instance I cannot seem to find any information of options other than question and about for QmessageBox. So if someone could also show me where some proper documentation lives it would prevent me asking too many questions here
Rather than PyQt documentation, it is better to directly use Qt documentation. You only need to switch your language mindset from Python to C++, there and back. It is not that difficult. :) See e.g. http://doc.qt.io/qt-4.8/qmessagebox.html#addButton or http://doc.qt.io/qt-4.8/qmessagebox.html#about I think this is very detailed documentation, unrivaled by most other frameworks.
Note that there are three overrides of addButton(). From the documentation it seems that you either need to pass two arguments to box.addButton(QPushButton('Close', self), QMessageBox.RejectRole) (you forgot the role!) or better, you use the override which uses standard buttons, then you only pass one argument: box.addButton(QMessageBox.Close).
And one more tip for you: I also find it easier to debug my program with PySide than PyQt because unlike PyQt, PySide catches the exception, prints that to console and keeps running. While PyQt usually just silently crashes leaving you clueless. Most of the time, I am using shims Qt.py https://pypi.python.org/pypi/Qt.py/0.6.9 or qtpy https://pypi.python.org/pypi/QtPy to be able to switch from PyQt to PySide on the fly. It also allows switching between Qt4 and Qt5 bindings easily.

Best Qt Widget to use for properties window... in PySide?

This question is the exact same as this one except that I'm looking for such a widget to be used in PySide. Anyone knows some code out there that provide easy-to-reuse property editor widget?
There is the proposal of using QTreeView or QTableView to build such a widget that is an option with PySide, but this is not a straight forward solution...
I don't know if you would be OK with this, but if you are happy to add the whole of pyqtgraph as a dependency, then you might want to try using pyqtgraph's ParameterTree.
There is a pretty comprehensive set of examples, just install pyqtgraph and then run:
import pyqtgraph.examples
pyqtgraph.examples.run()
Launch the ParameterTree example!

are there any cross platform window toolkits for python that aren't made by crazy people?

well maybe crazy is a bit too strong of a word, but what I am asking is if there are any window toolkits out there that don't have me do this:
class MyApp(SomeWindowClass):
I really don't want to use a library made by someone who is so obsessed with objects that he/she thinks that there should be a class for the app (which there will only be one instance of, so I don't see why anyone would want to do that extra typing)
(btw, no offense intended towards anyone who agrees with the way these libraries are set up, I just really want to know if there is anything out there with a tad bit less objects)
In general GUI toolkits rely on having some form of event loop running, the Application class in these toolkits is generally in charge of that event loop and marshaling events from the underlying window manager. Sure they could call the class EventLoopManager or something, but you need it either way so its just a naming thing then. In some cases though some toolkits who often use events can occasionally be used without them, and then you certainly dont want it to be some automatic thing.
There is PyQT.
Tkinter has one object per window/dialog, not app, and requires no classes to get something painted on the screen. It does, however, have its own main loop (like all the other GUI libraries). Obligatory Hello World:
from Tkinter import *
root = Tk()
w = Label(root, text="Hello, world!")
w.pack()
root.mainloop()
PyGTK another toolkit; which is python binding of Gtk. It is well structured with having excellent window and event loop system.
A typical example to show a window
import gtk
class Application:
def __init__(self):
self.window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
self.window.show()
if __name__ == "__main__":
app = Application()
gtk.mainloop()
Another recommendation is PyQt; which is python binding of Qt.
Typical hello world example is
import sys
from qt import *
app = QApplication(sys.argv)
myLabel = QLabel("Hello world!", None)
app.setMainWidget(myLabel)
myLabel.show()
app.exec_loop()
PyQt and PyGtk are widely used for rapid GUI development. From my experience pyGtk is poor in the online documentation/support compared to pyQt. But both are favorite of mine.
As you see with the answers above, GUI programming is almost always heavily object oriented, and there are good reasons for this: the graphical elements share a lot in terms of how they can be positioned within one-another, caring about whether the mouse pointer is over them etc. Furthermore, the C++ kits that qt, wx, gtk et al. wrap are already structured on a class/inheritance hierarchy, so you should not be surprised that the python wrappers are also.
If you want only simple GUI elements, then you may consider easyGUI (simple message boxes, text edit, choices), triatsUI (interactive object wrappers, primarily for controlling graphical objects) , which each solve some part of the GUI interactions without explicitly having you write GUI code.
For editing the values of fields in a record-like structure, you could also investigate GUIdata.
PS: there are various graphical tools out there to let you design your GUIs and link together some of the events, e.g., QtDesigner, that can help you avoid much of the tedious class definition code.

Categories

Resources