Context Menu not displaying correct language with PyQt5 - python

When I was trying to create a Qt application using PyQt5, I noticed that QPlainTextEdit standard context menu was being displayed in English , which is not the language of my system (Portuguese), despite its locale was correctly inherited from its parent widget. Is this the expected behavior? If so, how can i add a translation without having to rewrite the functions already present in that context menu (like cut/copy/paste)?
Example
This program reproduces the behavior described above; it shows a window (thus textEditor.locale().language() have the same value as QLocale.Portuguese) but the context menu is shown in english.
import sys
from PyQt5.QtWidgets import QApplication, QPlainTextEdit, QMainWindow
from PyQt5.QtCore import QLocale
def main():
app = QApplication(sys.argv)
window = QMainWindow()
assert(window.locale().language() == QLocale.Portuguese)
textEditor = QPlainTextEdit(window)
assert(textEditor.locale().language() == QLocale.Portuguese)
window.setCentralWidget(textEditor)
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

You need to install a QTranslator to add the translations for your system locale.
import sys
from PyQt5.QtWidgets import QApplication, QPlainTextEdit, QMainWindow
from PyQt5.QtCore import QLocale, QTranslator, QLibraryInfo
def main():
app = QApplication(sys.argv)
# Install provided system translations for current locale
translator = QTranslator()
translator.load('qt_' + QLocale.system().name(), QLibraryInfo.location(QLibraryInfo.TranslationsPath))
app.installTranslator(translator)
window = QMainWindow()
assert(window.locale().language() == QLocale.Portuguese)
textEditor = QPlainTextEdit(window)
assert(textEditor.locale().language() == QLocale.Portuguese)
window.setCentralWidget(textEditor)
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Related

PyQt5 WindowsVista style instead of Win10 style

When I design a UI with PyQt5, in Qt Designer it looks like win10 app. But when I start a program in VS Code or just run a file, it looks like in Windows Vista or 7. How to fix that? I haven't changed style in the code
import os
import sys
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow
class UI(QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi(os.path.join(sys.path[0], 'untitled.ui'), self)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = UI()
ex.show()
sys.exit(app.exec())
how it looks in designer
how it really looks
You can change the display style by using app.setStyle. To get a list of available styles on your system try this:
from PyQt5.QtWidgets import QStyleFactory
print(QStyleFactory.keys())
on my system I get:
['Breeze', 'Oxygen', 'Windows', 'Fusion']
Probably you'll get difference choices on a Windows box.
To evaluate different styles try:
app.setStyle('<style name here>')
or
app.setStyle(QStyleFactory.create('style name here'))

Add widget (QCheckBox) to QFileDialog in PyQt5 (Python) not working

I would like to add a QCheckBox to a QFileDialog. I would like to use the static method QFileDialog.getSaveFileName() to show the dialog.
I have found several similar questions, all in c++:
How to add checkbox to QFileDialog window in QT3?
Adding a widget in QFileDialog
https://www.qtcentre.org/threads/42858-Creating-a-Custom-FileOpen-Dialog
https://forum.qt.io/topic/103964/add-checkbox-to-qfiledialog/7
I did my best to translate these discussions to python, but have not gotten to the solution yet. My code runs, but the checkbox does not show up, even when I use QFileDialog.DontUseNativeDialog.
This is how I am subclassing QFileDialog:
from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtWidgets import QCheckBox
class ChkBxFileDialog(QFileDialog):
def __init__(self, chkBxTitle=""):
super().__init__()
self.setOption(QFileDialog.DontUseNativeDialog)
chkBx = QCheckBox(chkBxTitle)
self.layout().addWidget(chkBx)
#end __init__
#end ChkBxFileDialog
I have run this in two ways.
Option 1 (with extra QFileDialog.DontUseNativeDialog):
import sys
from PyQt5.QtWidgets import QApplication
if __name__ == "__main__":
app = QApplication(sys.argv)
fileDialog = ChkBxFileDialog(chkBxTitle="Chkbx")
fileName = fileDialog.getSaveFileName(filter='*.txt', initialFilter='*.txt',
options=QFileDialog.DontUseNativeDialog)[0]
sys.exit(app.exec_())
Option 2 (without extra QFileDialog.DontUseNativeDialog):
import sys
from PyQt5.QtWidgets import QApplication
if __name__ == "__main__":
app = QApplication(sys.argv)
fileDialog = ChkBxFileDialog(chkBxTitle="Chkbx")
fileName = fileDialog.getSaveFileName(filter='*.txt', initialFilter='*.txt')[0]
sys.exit(app.exec_())
The checkbox doesn't show with either option. Option 1 uses different window styling. Option 2 shows the typical PyQt QFileDialog.
Does anyone know what I am missing?
The problem is that getSaveFileName is a static method so they do not inherit from ChkBxFileDialog and therefore do not have the custom behavior.
There are 2 options:
Don't use getSaveFileName but implement the logic using QFileDialog directly:
import sys
from PyQt5.QtWidgets import QApplication, QCheckBox, QDialog, QFileDialog
class ChkBxFileDialog(QFileDialog):
def __init__(self, chkBxTitle="", filter="*.txt"):
super().__init__(filter=filter)
self.setSupportedSchemes(["file"])
self.setOption(QFileDialog.DontUseNativeDialog)
self.setAcceptMode(QFileDialog.AcceptSave)
self.selectNameFilter("*.txt")
chkBx = QCheckBox(chkBxTitle)
self.layout().addWidget(chkBx)
def main():
app = QApplication(sys.argv)
dialog = ChkBxFileDialog()
if dialog.exec_() == QDialog.Accepted:
filename = dialog.selectedUrls()[0].toLocalFile()
print(filename)
if __name__ == "__main__":
main()
Use some trick to get the QFileDialog instance, such as getting all the topLevels and verifying that it is a QFileDialog.
import sys
from functools import partial
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QCheckBox, QDialog, QFileDialog
def add_checkbox(chkBxTitle):
for tl in QApplication.topLevelWidgets():
if isinstance(tl, QFileDialog):
tl.setOption(QFileDialog.DontUseNativeDialog)
chkBx = QCheckBox(chkBxTitle)
tl.layout().addWidget(chkBx)
def main():
app = QApplication(sys.argv)
QTimer.singleShot(1000, partial(add_checkbox, ""))
fileName, _ = QFileDialog.getSaveFileName(
filter="*.txt", initialFilter="*.txt", options=QFileDialog.DontUseNativeDialog
)
if __name__ == "__main__":
main()
I would like to use the static method QFileDialog.getSaveFileName() to show the dialog
That's not possible. The static method as is defined in the C++ code knows nothing about your derived class, so it will create an instance of the base class, which doesn't contain your modifications. You have to explicitly instantiate your derived class, call exec() on the instance, check the return code and possibly call its selectedFiles() method to see what files were selected.

How to get size of widgets

I am developing a PyQt5 application however I am having issues with the heights of the widgets. Below is a simplified version of my issue:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class App(QWidget):
def __init__(self):
super().__init__()
self.showMaximized()
self.setStyleSheet("QWidget {background: blue;}")
print(self.frameGeometry().height())
self.show()
if __name__ == "__main__":
window = QApplication(sys.argv)
app = App()
window.setStyle(QStyleFactory.create("Fusion"))
window.exec_()
Here I create a window and maximise it. Using a tkinter window, it tells me the height maximised is 841, which is the size of my screen, however the PyQt5 application prints the height to be 519. Is this an issue with the self.showMaximized() method, or some other issue.
Resizing is not instantaneous in Qt. What Qt does is take the information from showMaximized to activate the flag of the native window (library that depends on each OS) then after a time T the OS applies that flag and sends it the new geometry. So in your case you have to give it a delay to get the correct information.
import sys
from PyQt5.QtWidgets import QApplication, QStyleFactory, QWidget
from PyQt5.QtCore import QTimer
class App(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet("QWidget {background: blue;}")
self.showMaximized()
QTimer.singleShot(100, self.calculate)
def calculate(self):
print(self.frameGeometry().height())
if __name__ == "__main__":
window = QApplication(sys.argv)
app = App()
window.setStyle(QStyleFactory.create("Fusion"))
window.exec_()
On the other hand, if your objective is to know the size of the initial screen then you should not use a QWidget for that since it will depend on the time it takes for Qt and the native library to create the native window, instead use the Screen class :
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QGuiApplication
if __name__ == "__main__":
window = QApplication(sys.argv)
print(QGuiApplication.primaryScreen().availableGeometry().height())

How to reboot PyQt5 application

I'm trying to restart my application after an update from the client side. I was able to achieve till the auto update part. I tried to surf on How to restart PyQt application?. There are few similar questions earlier, but none of them have good explanation or example with a button click event. Could you guys help me understand on how to reboot a PyQt application. Basically I want to restart the application from if __name__ == '__main__': everytime there is an update.
Note: AppLogin is my private module I created to handle application login. So basically that would be the landing QDialog once application is opened.
from PyQt5.QtWidgets import *
import sys
import AppLogin
class App:
def __init__(self):
btn = QPushButton(main_window)
btn.setText('close')
btn.pressed.connect(self.restart)
main_window.show()
def restart(self):
# Here goes the code for restart
pass
if __name__ == '__main__':
appctxt = QApplication(sys.argv)
log_in = AppLogin.Login()
if log_in.exec_() == QDialog.Accepted:
main_window = QMainWindow()
ui = App()
exit_code = appctxt.exec_()
sys.exit(exit_code)
The logic is to end the eventloop and launch the application an instant before it closes:
import sys
from PyQt5 import QtCore, QtWidgets
def restart():
QtCore.QCoreApplication.quit()
status = QtCore.QProcess.startDetached(sys.executable, sys.argv)
print(status)
def main():
app = QtWidgets.QApplication(sys.argv)
print("[PID]:", QtCore.QCoreApplication.applicationPid())
window = QtWidgets.QMainWindow()
window.show()
button = QtWidgets.QPushButton("Restart")
button.clicked.connect(restart)
window.setCentralWidget(button)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
A solution is to close (or forget) the QMainWindow and recreate it.
If you just "show()" a single widget, the same idea works fine.
import sys
import uuid
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
singleton: 'MainWindow' = None
def __init__(self):
super().__init__()
btn = QPushButton(f'RESTART\n{uuid.uuid4()}')
btn.clicked.connect(MainWindow.restart)
self.setCentralWidget(btn)
self.show()
#staticmethod
def restart():
MainWindow.singleton = MainWindow()
def main():
app = QApplication([])
MainWindow.restart()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

PyQt5 don't update TextEdit and Label Text [duplicate]

I'm newbie.
I want to click a pushButton to open a new window and take text from main window lineEdit and copy to new pop-up window lineEdit.
So far I an create new window but can't access lineEdit. No errors, app is not responding.
This is what I have:
from PyQt5.QtWidgets import QApplication
from PyQt5 import uic
app = QApplication([]) #Main Window
ui = uic.loadUi(r"D:\UI_test\gui\main_gui_TT.ui")
appedit = QApplication([]) #Pop-up
uiedit = uic.loadUi(r"D:\UI_test\gui\input_TT.ui")
def edit1():
uiedit.show()
appedit.exec_()
uiedit.lineEdit_CC.setText('text') <-this line is a problem
ui.pushButton_1edit.pressed.connect(edit1)
ui.show()
app.exec_()
Please help what is wrong here?
You should only have a single QApplication even if you have many windows, considering the above the solution is:
from PyQt5.QtWidgets import QApplication
from PyQt5 import uic
app = QApplication([]) # Main Window
ui = uic.loadUi(r"D:\UI_test\gui\main_gui_TT.ui")
uiedit = uic.loadUi(r"D:\UI_test\gui\input_TT.ui")
def edit1():
uiedit.show()
uiedit.lineEdit_CC.setText("text")
ui.pushButton_1edit.pressed.connect(edit1)
ui.show()
app.exec_()

Categories