Changing font size of all QLabel objects PyQt5 - python

I had written a gui using PyQt5 and recently I wanted to increase the font size of all my QLabels to a particular size. I could go through the entire code and individually and change the qfont. But that is not efficient and I thought I could just override the class and set all QLabel font sizes to the desired size.
However, I need to understand the class written in python so I can figure out how to override it. But I did not find any python documentation that shows what the code looks like for QLabel. There is just documentation for c++. Hence, I wanted to know where I can get the python code for all of PyQt5 if that exists? If not, how can I change the font size of all QLabels used in my code?

To change the font of all QLabels then there are several options:
Use Qt StyleSheet
app.setStyleSheet("QLabel{font-size: 18pt;}")
Use QApplication::setFont()
custom_font = QFont()
custom_font.setWeight(18);
QApplication.setFont(custom_font, "QLabel")

While the provided answers should have already given you enough suggestions, I'd like to add some insight.
Are there python sources for Qt?
First of all, you cannot find "the class written in python", because (luckily) there's none. PyQt is a binding: it is an interface to the actual Qt library, which is written in C++.
As you might already know, while Python is pretty fast on nowadays computers, it's not that fast, so using a binding is a very good compromise: it allows the simple syntax Python provides, and gives all speed provided by C++ compiled libraries under the hood.
You can find the source code for Qt widgets here (official mirror), or here.
How to override the default font?
Well, this depends on how you're going to manage your project.
Generally speaking, you can set the default font [size] for a specific widget, for its child widgets, for the top level window or even for the whole application. And there are at least two ways to do it.
use setFont(): it sets the default font for the target; you can get the current default font using something.font(), then use font.setPointSize() (or setPointSizeF() for float values, if the font allows it) and then call setFont(font) on the target.
use font[-*] in the target setStyleSheet();
Target?
The target might be the widget itself, one of its parents or even the QApplication.instance(). You can use both setFont() or setStyleSheet() on any of them:
font = self.font()
font.setPointSize(24)
# set the font for the widget:
self.pushButton.setFont(someFont)
# set the font for the top level window (and any of its children):
self.window().setFont(someFont)
# set the font for *any* widget created in this QApplication:
QApplication.instance().setFont(someFont)
# the same as...
self.pushButton.setStyleSheet(''' font-size: 24px; ''')
# etc...
Also, consider setting the Qt.AA_UseStyleSheetPropagationInWidgetStyles attribute for the application instance.
Setting and inheritance
By default, Qt uses font propagation (as much as palette propagation) for both setFont and setStyleSheet, but whenever a style sheet is set, it takes precedence, even if it's set on any of the parent widgets (up to the top level window OR the QApplication instance).
Whenever stylesheets are applied, there are various possibilities, based on CSS Selectors:
'font-size: 24px;': no selector, the current widget and any of its child will use the specified font size;
'QClass { font-size: 24px; }': classes and subclasses selector, any widget (including the current instance) and its children of the same class/subclass will use the specified font size:
'QClass[property="value"] {...}': property selector, as the above, but only if the property matches the value; note that values are always quoted, and bool values are always lower case;
'.QClass {...}': classes selector, but not subclasses: if you're using a subclass of QLabel and the stylesheet is set for .QLabel, that stylesheet won't be applied;
'QClass#objectName {...}': apply only for widgets for which objectName() matches;
'QParentClass QClass {...}': apply for widget of class QClass that are children of QParentClass
'QParentClass > QClass {...}': apply for widget of class QClass that are direct children of QParentClass
Note that both setFont and setStyleSheet support propagation, but setStyle only works on children when set to the QApplication instance: if you use widget.setStyle() it won't have effect on any of the widget's children.
Finally, remember:
whenever a widget gets reparented, it receives the font, palette and stylesheet of its parent, in "cascading" mode (the closest parent has precedence);
stylesheets have precedence on both palette and font, whenever any of the related properties are set, and palette/font properties are not compatible with stylesheets (or, at least, they behave in unexpected ways);

Qt Stylesheets
This is probably the easiest way to do in your situation, you are really trying to apply a specific "style" to all your QLabels. You can apply a style to your whole application, or a specific window, and this will affect all children that match the selectors.
So in your case, to apply to all widgets in your application you can do the following to set the font size of all QLabel instances:
app = QApplication([])
app.setStyleSheet('.QLabel { font-size: 14pt;}')
Note: Be sure to set the stylesheet before attaching your widgets to its parent, otherwise you would need to manually trigger a style refresh.
Also...
The .QLabel selector will only apply to QLabel class instances, and not to classes that inherit QLabel. To apply to both QLabel and inherited classes, use QLabel {...} instead of .QLabel {...} in the stylesheet.
Some documentation to help you beyond that:
Qt stylesheet documentation: https://doc.qt.io/qt-5/stylesheet.html
Qt stylesheet syntax: https://doc.qt.io/qt-5/stylesheet-syntax.html
Qt stylesheet reference: https://doc.qt.io/qt-5/stylesheet-reference.html
PyQt documentation: https://doc.qt.io/qtforpython/api.html

Completing Adrien's answer, you can use QFont class and perform .setFont() method for every button.
my_font = QFont("Times New Roman", 12)
my_button.setFont(my_font)
Using this class you can also change some font parameters, see https://doc.qt.io/qt-5/qfont.html
Yeah, documentation for C++ is okay to read because all methods & classes from C++ are implemented in Python.
UPD: QWidget class also has setFont method so you can set font size on centralwidget as well as using stylesheets.

Related

Select Directory with Python

I want to select 5 images with Python so that I can use these imges in my python program. I tried to do this with QFileDialog() (PyQt5) but I only succeed to single select a file.
And how to select a folder is also not really comprehensive.
I just want to select 5 images and select a folder so that I can save files in that folder. But it seems to be not so easy to do that.
I really like Python because its so easy but PyQt5 makes me everytime I use it just aggressive, all other libraries are just nice and easy to understand.
Maybe there is a good alternative to pyqt? tkinter maybe?
thanks.
In order to select a folder you can use this code:
widget = QWidget()
dialog = QFileDialog(
widget, "Select Directory of the Desired Files", os.path.curdir
)
dialog.setFileMode(QFileDialog.DirectoryOnly)
Qt supplies a bunch of static methods to get standardized file dialogs, two of them already satisfy your needs: getOpenFileNames() (stress on the final "s") and getExistingDirectory().
The first will return a list of absolute paths of selected file[s], the last will return the selected directory.
I know that reading the official documentation might be a bit overwhelming if you don't know anything about C++ (they are explained in detail, though), but they're not as hard as one could think.
Every function is listed in a very simple way:
returned_type : function_name(arguments) [const -> you can usually ignore this]
The returned_type is the type of the value the function is expected to return. In "c++ slang", void is the same as return (or return None or no return at all, as Python implicitly returns None if no other value/object is returned at the end of a function), if the type is a QString it's automatically converted to a Python str, while qreal is the same as Python's floats,. This is very important for "private" functions (methods), which are internally used by Qt: if you are subclassing and want to override a private method of a Qt class, you have to return the type Qt expects. You could theoretically ignore the returned_type for public functions if you know what you're doing, but it's usually better to stick with the original type.
There are some small "exceptions" that require some consideration. In some cases Qt expects some argument that will be modified within the function and would normally return whether the function was successful or not, while in Python it might return the reference to the argument (sorry, I can't remember them right now). Some other functions return a tuple instead of a single value, and that's the case of some static QFileDialog functions such as getOpenFileName[s] which return both the selected file[s] and the selected filter.

How to detect the type of widget?

This should be a stupid question. I am just curious and could not find the answer on my own.
E.g. I define in PyQt5 some widgets:
self.lbl = QLabel("label")
self.btn = QPushButton("click")
self.txt = QLineEdit("text")
Is there any method to detect what kind of widget the self.lbl, self.btn, or self.txt are?
I could imagine: by detecting the widget type, the input is self.lbl, the output should be QLabel... Or something like this.
I have only found the isWidgetType() method. But it is not what I want to have.
There are several ways to get the name of the widget:
using __class__:
print(self.lbl.__class__.__name__)
using QMetaObject:
print(self.lbl.metaObject().className())
These previous methods return a string with the name of the class, but if you want to verify if an object belongs to a class you can use isinstance():
is_label = isinstance(self.lbl, QLabel)
Another option is to use type() but it is not recommended, if you want to get more information about isinstance() and type() read the following: What are the differences between type() and isinstance()?
You can just use the standard Python means of checking an object type:
print(type(self.lbl))
print(isinstance(self.lbl, QLabel)

Python ttk Object - Not understanding Widget-Specific Options

I was implementing a ttk progress bar yesterday and saw some code that I didn't quite understand.
A maximum value can be set for a progress bar by using something like the following:
progress_bar["maximum"] = max
I was expecting the ttk Progressbar object would use an instance variable to track the maximum value for a created object, but that syntax would look more like:
progres_bar.maximum = max
So my question is, what exactly is happening with the bracketed syntax here, what's the terminology, and where can I read more on this? When I looked at the Progressbar class, all I saw was
class Progressbar(Widget):
"""Ttk Progressbar widget shows the status of a long-running
operation. They can operate in two modes: determinate mode shows the
amount completed relative to the total amount of work to be done, and
indeterminate mode provides an animated display to let the user know
that something is happening."""
def __init__(self, master=None, **kw):
"""Construct a Ttk Progressbar with parent master.
STANDARD OPTIONS
class, cursor, style, takefocus
WIDGET-SPECIFIC OPTIONS
orient, length, mode, maximum, value, variable, phase
"""
Widget.__init__(self, master, "ttk::progressbar", kw)
I see there's a "widget-specifc option", but I don't understand how progress_bar["maximum"] = max sets that value, or how it's stored.
What is happening is that the ttk module is a thin wrapper around a tcl interpreter with the tk package installed. Tcl/tk has no concept of python classes.
In tcl/tk, the way to set an attribute is with a function call. For example, to set the maximum attribute, you would do something like this:
.progress_bar configure -maximum 100
The ttk wrapper is very similar:
progress_bar.configure(maximum=100)
For a reason only known to the original tkinter developers, they decided to implement a dictionary interface that allows you to use bracket notation. Maybe they thought it was more pythonic? For example:
progress_bar["maximum"] = 100
Almost certainly, the reason they didn't make these attributes of the object (eg: progress_bar.maximum = 100) is because some tcl/tk widget attributes would clash with python reserved words or standard attributes (for example, id). By using a dictionary they avoid such clashes.
Widget extends Tkinter.Widget extends BaseWidget extends Misc which contains:
__getitem__ = cget
def __setitem__(self, key, value):
self.configure({key: value})
You can find this in your Python's library folder; search for Tkinter.py
This is the code which implement the dict interface. There is no implementation for attribute access which would use __getattr__() and __setattr__().
As to why the Tk guys went this way? It's hard to say. Tk integration in Python is pretty old. Or the people felt that __getattr__() with its quirks would cause more trouble.

GUI design - display messages without popups

What techniques do you use to display messages in a GUI without popup dialogs?
Popup dialogs are generally quite horrible for a user - they get in the way and often you are not interested that you just caused an error. An alternative is just to ignore errors and do nothing
However, there may be the occasional user who wants to know when they caused an error...
So you want to display informative messages but without requiring that the user has to click away annoying popup boxes.
One option could be to use a statusbar of the mainwindow, but in order for any widget to use it, you need to pass around references to this damn statusbar (I'm thinking of python/qt here)...it quickly gets confusing and removes 'resuability' of your widgets (imagine you create another app, without a statusbar and you want to reuse a widget in it...)...
Any ideas?
One option could be to use a statusbar of the mainwindow, but in order for any widget to use it, you need to pass around references to this damn statusbar
Designed correctly, this is not the case. Many of my classes have a Log signal like this:-
void Log(const QString& message, enum LogPriority priority);
The priority is an enum, used to define the level of information, whether it's a debug message, warning, error, critical error etc.
In addition, I have a Logging class with a matching Log slot. You could make this a singleton, or simply have a static method.
Classes connect their signals either directly to the logging class, or to a parent's signal. This ensures that the class doesn't care what happens when it sends a log message. You can also disable a class from logging by removing its connect.
As for the logging itself, the Log class can choose to either set the text on the message bar, write to a file, display a notification (OSX) or any other method you want.
While my method uses C++, I expect you can do the same or similar in Python.
What I've done in my QT/C++ application, is a QDockedWidget in the main window called "Message Board", containing a list of warning/error/info messages. It could be anyway removed by the user. To do not pass a reference to all the widgets of this QDockedWidget, I use (for many other purpose too...) a SharedData class, with global visibility, built as singleton for the application. So every widget as a global reference to it and can set an error or warning or something else:
Gshared->setError("oops!", ErrorType::Critical);
In setError function here I emit a signal, that is catched by a slot in the QDockedWidget (for displaying the error), by a logger manager (that writes in a log file more details about the error), etc...
An another option would be the "do not show again" checkbox in a custom messageBox.
First of all, the message and the display widget for it are two separate things. It's a serious design error to mangle them together.
A typical solution would have some sort of a logger/message sink that is a semantically a singleton, but not necessarily implemented using the singleton pattern. The sink could be a QObject so that you can easily connect message sources to the sink. The sink can then be attached to one or more display widgets.
Passing the sink around is very easy thanks to QObject and the fact that qApp is a global instance pointer, and QCoreApplication is a QObject. Thus you can:
pass the pointer via the dynamic property system, or
make the sink a sole child of the global application object.
See example below. Note that the Widget only needs to know the declaration of the MessageSink class. It doesn't need to be passed any instances explicitly. Neither is the usual singleton pattern used as-is.
class MessageSink : public QObject {
Q_OBJECT
public:
MessageSink(QObject * parent = 0) : QObject(parent) {}
Q_SIGNAL void message(const QString &);
static MessageSink * instance() { return qApp->findChild<MessageSink*>(); }
}
class Widget : public QWidget {
QVBoxLayout m_layout;
QLabel m_l1, m_l2;
public:
Widget(QWidget * parent = 0) : QWidget(parent), m_layout(this) {
m_layout.addWidget(&m_l1);
m_layout.addWidget(&m_l2);
m_l1.connect(MessageSink::instance(), SIGNAL(message(QString)), SLOT(setText(QString)));
m_l2.connect(MessageSink::instance(), SIGNAL(message(QString)), SLOT(setText(QString)));
}
}
int main(int argc, char ** argv) {
QApplication app(argc, argv);
MessageSink sink(&app);
Widget w;
w.show();
emit sink.message("Hello!");
return app.exec();
}
Note: It is not a bug to have a local QObject that has a parent. You just have to make sure it gets destructed before the parent. C++ guarantees that here.

How to get all child components of QWidget in pyside/pyqt/qt?

I am developing a desktop application using pyside(qt), I want to access(iterate) all line edit components of QWidget. In qt I found two methods findChild and findChildren but there is no proper example found and My code shows error, 'form' object has no attribute 'findChild'.
Here 'form' is Qwidget form consist components lineEdit, comboboxes, Qpushbuttons etc.
Code:
lineEdits = form.findChild<QLineEdit>() //This is not working
lineEdits = form.findChild('QLineEdit) //This also not working
The signatures of findChild and findChildren are different in PySide/PyQt4 because there is no real equivalent to the C++ cast syntax in Python.
Instead, you have to pass a type (or tuple of types) as the first argument, and an optional string as the second argument (for matching the objectName).
So your example should look something like this:
lineEdits = form.findChildren(QtGui.QLineEdit)
Note that findChild and findChildren are methods of QObject - so if your form does not have them, it cannot be a QWidget (because all widgets inherit QObject).
Use this method QObject::findChildren(onst QString & name = QString()) with no parameters.
Omitting the name argument causes all object names to be matched.
Here is C++ example code:
QList<QLineEdit*> line_edits = form.findChildren<QLineEdit*>();

Categories