I recently switched to windows 11 and decided to start a QyQt5 Project, I started to add buttons, which worked. But when I try to type in the textbox it will not display text and not type anything. The only thing it showed was that I had highlighted the textbox. Sadly there is no error or logs showing why it is behaving like this.
This is what happens:
And this is the code that I have used:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(332, 121)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.Download = QtWidgets.QPushButton(self.centralwidget)
self.Download.setGeometry(QtCore.QRect(240, 70, 75, 23))
self.Download.setObjectName("Download")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(20, 70, 75, 23))
self.pushButton.setObjectName("pushButton")
self.linkbox = QtWidgets.QPlainTextEdit(self.centralwidget)
self.linkbox.setGeometry(QtCore.QRect(23, 30, 291, 21))
self.linkbox.setToolTipDuration(-5)
self.linkbox.setLayoutDirection(QtCore.Qt.LeftToRight)
self.linkbox.setInputMethodHints(QtCore.Qt.ImhMultiLine|QtCore.Qt.ImhNoEditMenu)
self.linkbox.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.linkbox.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.linkbox.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.linkbox.setTabChangesFocus(True)
self.linkbox.setPlainText("")
self.linkbox.setObjectName("linkbox")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "ChaosCapture"))
self.Download.setText(_translate("MainWindow", "Download"))
self.pushButton.setText(_translate("MainWindow", "Options"))
self.linkbox.setPlaceholderText(_translate("MainWindow", "Enter the download link here"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
You're using a very small height for the QPlainTextEdit, and it's completely covered by the horizontal scroll bar (which you've set as always on).
You are also not using layout managers, which is highly discouraged. For instance, the user can resize the window and make it smaller, so the widgets can become partially visible or completely hidden. Layout managers should be always preferred against predetermined geometries.
There are three things that should be done:
set a layout in Designer, in your case a grid layout would be fine: right click on an empty area of the window, open the "Lay out" menu and select "Lay Out in a Grid";
set an arbitrary minimum height for the plain text edit (so that you can resize the window in designer);
in the program script, set a minimum height for the widget based on the font metrics;
Note that the program script must be a separate file, not the one you're showing us (the pyuic file), which must never be manually edited for any reason (read more about using Designer).
The following code assumes that the pyuic file is named ui_mainwindow.py.
from PyQt5 import QtWidgets
from ui_mainwindow import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.linkbox.setMinimumHeight(
self.fontMetrics().height() +
self.linkbox.frameWidth() * 2 +
self.linkbox.document().documentMargin() * 2 +
self.linkbox.horizontalScrollBar().sizeHint().height()
)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
Note: hiding the vertical scroll bar is not a very good idea. You either use a QLineEdit, or you should at least make the QPlainTextEdit a bit higher (self.fontMetrics().height() * 2 + ...), otherwise it would be very confusing if the user presses the Enter key by mistake or pastes text with new lines.
Related
This question already has answers here:
QtDesigner changes will be lost after redesign User Interface
(2 answers)
Closed 2 years ago.
I'm new to Python and I've searched for an answer but couldn't find it (or rather couldn't properly implement it).
I've generated a window with a few buttons in QtDesigner's file named "arch.ui", converted to arch.py.
As I'll be updating GUI occasionally, I don't want to create functions in arch.py, so I've created a main.py file for that.
I've a problem with linking button click to a function => I try to link "btnSource" (from arch.py) to function "printMe" (in main.py).
Obviously it doesn't work. Any help welcome.
Here is generated Designer file:
# Form implementation generated from reading ui file 'arch.ui'
#
# Created by: PyQt5 UI code generator 5.15.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(460, 233)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.btnSource = QtWidgets.QPushButton(self.centralwidget)
self.btnSource.setGeometry(QtCore.QRect(80, 60, 75, 23))
self.btnSource.setObjectName("btnSource")
self.lblSource = QtWidgets.QLabel(self.centralwidget)
self.lblSource.setGeometry(QtCore.QRect(180, 60, 511, 21))
self.lblSource.setObjectName("lblSource")
self.lblTarget = QtWidgets.QLabel(self.centralwidget)
self.lblTarget.setGeometry(QtCore.QRect(180, 120, 481, 16))
self.lblTarget.setObjectName("lblTarget")
self.btnTarget = QtWidgets.QPushButton(self.centralwidget)
self.btnTarget.setGeometry(QtCore.QRect(80, 120, 75, 23))
self.btnTarget.setObjectName("btnTarget")
self.btnGo = QtWidgets.QPushButton(self.centralwidget)
self.btnGo.setGeometry(QtCore.QRect(280, 120, 75, 23))
self.btnGo.setObjectName("btnGo")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 460, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.btnSource.setText(_translate("MainWindow", "Source"))
self.lblSource.setText(_translate("MainWindow", "TextLabel"))
self.lblTarget.setText(_translate("MainWindow", "TextLabel"))
self.btnTarget.setText(_translate("MainWindow", "Target"))
self.btnGo.setText(_translate("MainWindow", "Go"))
And here is my main.py file:
from PyQt5 import QtCore, QtGui, QtWidgets
from arch import Ui_MainWindow
import sys
app = QtWidgets.QApplication(sys.argv)
class myWindow(Ui_MainWindow):
def __init__(self):
super(myWindow, self).__init__()
self.btnSource.clicked.connect(self.btnSource.printMe)#
def printMe(self):
print('blah blah blah')
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
tl;dr
Subclass from both QMainWindow and Ui_MainWindow, and call setupUi from there; then create an instance of myWindow:
class MyWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super(MyWindow, self).__init__()
self.setupUi(self)
self.btnSource.clicked.connect(self.printMe)
def printMe(self):
print('blah blah blah')
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
mainWindow = MyWindow()
mainWindow.show()
sys.exit(app.exec_())
Explanation
Your code doesn't work for many reasons; while the main problem might be that you actually never created an instance of myWindow (about that, you should always use capitalized names for classes), making it completely useless, it wouldn't have worked anyway.
That's because you should not subclass from the ui class object, but from the QWidget descendant (QMainWindow, in your case) you're going to use.
The ui_* objects created from pyuic are only intended as a high level (and unmodified) interface to create the UI on top of a QWidget subclass.
Calling setupUi(something) actually creates all child widgets for the widget something, sets the layout and, possibly, automatically connects to slots with a compatible name, but that's all: in fact, if you closely look at the code from the ui file, it actually does nothing besides setupUi and retranslateUi (nor it should!): there's not even an __init__!
If you need to add interaction and create connections from signals to slot/functions, you should use the single/multiple inheritance approaches as explained in the official guide about using Designer with PyQt; the only other possibility is to use loadUi (while still subclassing from the base class) with the source .ui file:
from PyQt5 import QtWidgets, uic
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi('path/to/gui.ui', self)
self.someWidget.someSignal.connect(self.someSlot)
# ...
def someSlot(self, signalArguments, [...]):
# do something...
PS: for various reasons, it's usually better to run a QApplication only if the script is the one that's been run (hence the if __name__ ...), mostly because there should be just only one QApplication instance for every running program; in any case, it shouldn't be created before the class declarations (unless, you really know what you're doing); it's not a big deal in your case, but, as usual, better safe than sorry.
My application, created with Pyinstaller, worked fine until I upgraded from High Sierra to Mojave. In order to demonstrate the issue I create the simple application.
Main window has only one button. When you press the button its text should be changed to "Please wait" for 10 seconds.
When I run this program as .py script, everything works fine, but after creating .app file with Pyinstaller it behaves differently. The text is not updated until you click anywhere outside of the window.
I tried to reinstall Pyinstaller, but the problem still exists.
from PyQt5 import QtCore, QtGui, QtWidgets
import time
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(303, 304)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(50, 80, 300, 43))
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(80, 170, 113, 32))
self.pushButton.setObjectName("pushButton")
self.pushButton.setDefault(True)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.pushButton.clicked.connect(self.click)
self.thread = Thread()
self.thread.finished.connect(lambda: self.pushButton.setEnabled(True))
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Click me"))
def click(self):
if not self.thread.isRunning():
self.pushButton.setEnabled(False)
self.pushButton.setText("Please wait")
self.label.setText("The button below should display \n 'Please wait' for 10 seconds")
self.thread.start()
class Thread(QtCore.QThread):
def run(self):
time.sleep(10)
ui.pushButton.setEnabled(False)
ui.pushButton.setText("Click me")
ui.label.setText("")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
I found an answer to my question. In order to solve this rendering issue you need to add the following line for a ui element, which needs to be updated. In my case it is required only if I need to run this application on macOS Mojave.
<element>.repaint()
For example:
def click(self):
self.pushButton.setEnabled(False)
self.pushButton.setText("Button is clicked...")
self.pushButton.repaint()
I have a block of text in my QTextEdit area but it is too big for the area. I have tried adding the textArea to a QScrollArea to enable me to scroll through the text. I am wondering where I am going wrong with this as it does not seem to do anything. I am wondering what is the correct thing to do. Here is my code with the outputted stack trace.
# Import Statements
from PyQt5 import QtCore, QtGui, QtWidgets
from MainGuiWindow import Ui_MainWindow
# Main Class that holds User Interface Objects
class Ui_informationWindow(object):
# Function for Opening Main GUI window from login window by clicking login button
def openMainWindow(self):
self.window = QtWidgets.QMainWindow()
self.ui = Ui_MainWindow()
self.ui.setupUi(self.window)
loginWindow.hide()
self.window.show()
def setupUi(self, informationWindow):
informationWindow.setObjectName("User Information Window")
informationWindow.setFixedSize(393, 300)
self.centralwidget = QtWidgets.QWidget(informationWindow)
self.centralwidget.setObjectName("centralwidget")
informationWindow.setStyleSheet("background-color: Cornflowerblue")
# Proceed button
self.proceedButton = QtWidgets.QPushButton(self.centralwidget)
self.proceedButton.setGeometry(QtCore.QRect(280, 250, 101, 27))
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(12)
font.setBold(True)
font.setWeight(75)
self.proceedButton.setFont(font)
self.proceedButton.setStyleSheet("background-color: Silver")
self.proceedButton.setObjectName("proceedButton")
# passwordTF
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
self.textEdit.setGeometry(QtCore.QRect(20, 10, 350, 230))
self.textEdit.setObjectName("informationTF")
informationWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(loginWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 393, 21))
self.menubar.setObjectName("menubar")
informationWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(informationWindow)
self.statusbar.setObjectName("statusbar")
self.textEdit.setStyleSheet("background-color: White")
# Set Scroll Area
self.textEdit.append("This is an information manual to guide users through the use of the software.")
self.textEdit.append("\n")
self.textEdit.append("To use this software, the user must first parse the data from each evidence item which will store the data in Microsoft Excel table format in an external directory")
self.textEdit.append("In turn, the corresponding report generation options will become available one by one to enable report generation for that piece of evidence.")
self.textEdit.append("\n")
self.textEdit.append("Once each report has been generated, the user can then view a single report for one piece of evidence or view a full report containing all parsed evidence.")
self.textEdit.append("\n")
self.textEdit.append("These reports, much like the parsed forensic data are stored in a Forensic Reports directory.")
self.textEdit.append("\n")
self.textEdit.append("Please press the Proceed button to begin using the software.")
self.textEdit.setFont(font)
self.textEdit.setEnabled(False)
# This is where I am trying to put the textEdit text into a scrollArea to make it scrollable
self.scrollArea = QtWidgets.QScrollArea()
self.textEdit.setVerticalScrollBar(self.textEdit)
informationWindow.setStatusBar(self.statusbar)
self.retranslateUi(informationWindow)
QtCore.QMetaObject.connectSlotsByName(informationWindow)
# Function that sets the text on all the UI Buttons
def retranslateUi(self, loginWindow):
_translate = QtCore.QCoreApplication.translate
loginWindow.setWindowTitle(_translate("informationWindow", "User Manual"))
self.proceedButton.setText(_translate("informationWindow", "Proceed"))
# Event Handling Code Section
# Event Handling to open Main GUI Window
self.proceedButton.clicked.connect(self.openMainWindow)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
loginWindow = QtWidgets.QMainWindow()
ui = Ui_informationWindow()
ui.setupUi(loginWindow)
loginWindow.show()
sys.exit(app.exec_())
Stack Trace
self.textEdit.setVerticalScrollBar(self.textEdit)
TypeError: QAbstractScrollArea.setVerticalScrollBar(QScrollBar): argument 1 has unexpected type 'QTextEdit'
QTextEdit already has a QScrollBar so you should not add any, if you want the text not to be editable you should use setReadOnly(True) instead of setEnabled(False).
# passwordTF
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
[...]
self.textEdit.setFont(font)
self.textEdit.setReadOnly(True)
I'm trying to use an interface I designed in Qt designer as a means of letting the user edit preferences for my program.
I am currently able to display the GUI I made by connecting the following function to the preferences menu option:
def preferences(self):
preferences_dialog = QtGui.QDialog()
preferences_dialog.ui = Ui_Preferences()
preferences_dialog.ui.setupUi(preferences_dialog)
preferences_dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)
preferences_dialog.exec_()
My question is this: what is a good way to implement this so that I can use the fields in the GUI to change values in my config file?
I also want to display the pre-existing values in the boxes before they are changed.
Should I make a new class that uses the above function as its __init__ method? I would imagine I might need a class that handles all of the processes for the window. Also, I am unsure of a good way to pass data between the file and the GUI without a bunch of specific code.
Use QSettings. Here's an example in PyQt5.
First, the main window's ui definition
# file ui_main.py
from PyQt5 import QtCore, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(584, 897)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 584, 21))
self.menubar.setObjectName("menubar")
self.menuPreferences = QtWidgets.QMenu(self.menubar)
self.menuPreferences.setObjectName("menuPreferences")
MainWindow.setMenuBar(self.menubar)
self.setPreferencesAction = QtWidgets.QAction(MainWindow)
self.setPreferencesAction.setObjectName("setPreferencesAction")
self.menuPreferences.addAction(self.setPreferencesAction)
self.menubar.addAction(self.menuPreferences.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.menuPreferences.setTitle(_translate("MainWindow", "Settings"))
self.setPreferencesAction.setText(_translate("MainWindow", "Preferences"))
and second, the preferences dialog ui definition:
# file ui_dialog.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(508, 300)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setGeometry(QtCore.QRect(150, 250, 341, 32))
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.sl_value = QtWidgets.QSlider(Dialog)
self.sl_value.setGeometry(QtCore.QRect(220, 120, 161, 31))
self.sl_value.setOrientation(QtCore.Qt.Horizontal)
self.sl_value.setObjectName("sl_value")
self.buttonBox.accepted.connect(Dialog.accept)
self.buttonBox.rejected.connect(Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
The MainWindow keeps track of the configuration in a QSettings object, which is uniquely defined (and accessed) using the application and company strings fed into its constructor.
# file main.py
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.settings = QSettings(COMPANY_NAME, APPLICATION_NAME)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
when the preferences dialog box is triggered, the settings are loaded and passed along to the PreferencesDialog. If the Dialog returns successfully, the new settings are saved and written to storage using del
#pyqtSlot(bool)
def on_setPreferencesAction_triggered(self, triggered):
settings = self.settings
default_config_value = settings.value(CONFIG_KEY_1, defaultValue=None, type=str)
preference_dialog = PreferencesDialog(default_config_value=default_config_value, parent=self)
if preference_dialog.exec():
settings.setValue(CONFIG_KEY_1, preference_dialog.preferences[CONFIG_KEY_1])
# this writes the settings to storage
del settings
The PreferencesDialog constructor sets the values according to the parameters it receives, and a a pyqtSlot is attached to the appropriate signal to save the values in a dictionary.
To run the demo:
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
I would like to display a window with a QwebView widget in Pyside. For this I use some code generated by QtCreator:
#code generated by QtCreator:
from PySide import QtCore, QtGui
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(400, 300)
self.centralWidget = QtGui.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.webView = QtWebKit.QWebView(self.centralWidget)
self.webView.setGeometry(QtCore.QRect(10, 20, 380, 270))
self.webView.setUrl(QtCore.QUrl("file:///C:/pdf_folder/test.pdf"))
self.webView.setObjectName("webView")
MainWindow.setCentralWidget(self.centralWidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
from PySide import QtWebKit
# My code:
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
if __name__ == "__main__":
APP = QtGui.QApplication(sys.argv)
MW = MainWindow()
MW.show()
sys.exit(APP.exec_())
I am sure to have a pdf file in the specified path, but when I run my script, the pdf file is never displayed in my window.
Do you know what I am doing wrong ?
I saw this topic Is it possible to get QWebKit to display pdf files?, but the answer don't work for me (after changing PyQt to Pyside in the import lines).
I also saw this topic PDF with QWebView: missing refresh/repaint after loading, the workaround (using a timer before loading) work for me. however I don't think that using a timer to load a file is a good way to solve the problem.
And, mainly, the code that I used for Ui_MainWindow is generated with QtCreator, I didn't change it and I don't want to change it by myself (without using QtCreator). It is just a simple form for a window with only one widget QtWebKit.QWebView in which I want to load a file. It should work without weird workaround. Why the code automatically generated don't work ? Am I using QtCreator in a wrong way ?
You have to enable QWebView's plugins:
self.webView = QtWebKit.QWebView(self.centralWidget)
self.webView.settings().setAttribute(QWebSettings.PluginsEnabled, True)
also try to set the QWebView URL after showing the main window.
MW = MainWindow()
MW.show()
MW.ui.webView.setUrl(QtCore.QUrl("file:///C:/pdf_folder/test.pdf"))
I think the paint events has to do with the fact you don't see the pdf file.