I have a QListWidget containing one item, I want the item's texts to be partially bold, so I created a QItemWidget for it. I use a QLabel in it to display the partially bold texts, the code is as follows:
ItemWidget.py
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QHBoxLayout, QLayout, QLabel
class ItemWidget(QtWidgets.QWidget):
def __init__(self):
super(ItemWidget, self).__init__()
self.setLayout(self.__get_layout())
def __get_layout(self) -> QHBoxLayout:
label = QLabel("<b>bold</b> texts")
widget_layout = QHBoxLayout()
widget_layout.addWidget(label)
widget_layout.setSizeConstraint(QLayout.SetFixedSize)
widget_layout.setContentsMargins(0, 0, 0, 0)
return widget_layout
Main.py
from PyQt5.QtWidgets import QListWidget, QWidget, QVBoxLayout, QApplication, QListWidgetItem
from ItemWidget import ItemWidget
class Window(QWidget):
def __init__(self):
super().__init__()
list_widget = QListWidget()
list_widget.itemClicked.connect(self.on_item_clicked)
vbox = QVBoxLayout()
vbox.addWidget(list_widget)
item_widget = ItemWidget()
item = QListWidgetItem()
list_widget.addItem(item)
list_widget.setItemWidget(item, item_widget)
self.setLayout(vbox)
def on_item_clicked(self):
print("Item clicked")
app = QApplication([])
window = Window()
window.show()
app.exec_()
This is the UI:
When I click on the item in the list, Item clicked should be printed to the output console. But the problem is, it only works if I don't click on the text:
The problem seems to disappear if I give a plain text to the QLabel in ItemWidget, such as label = QLabel("text"), why? What's the problem here?
Setting rich text content causes the mouse button release event to be always accepted (I suppose it's due to the fact that rich text can contain links).
The view must receive a mouse release event in order to call its mouseReleaseEvent() and eventually emit the clicked signal, but since the event was already accepted by the label, this won't happen.
If you don't need links in that text, just reset the text interaction flags:
label.setTextInteractionFlags(Qt.NoTextInteraction)
Related
How do I create a drop-down widget, such as a drop-down QLabel, drop-down QTextBrowser, etc.?
For example, I log information in a QTextBrowser, but I don't want it taking up space on the screen. So I want to be able to click a QToolbutton and have a scrollable QTextBrowser drop-down. (A QComboBox would work too, but I can't just add each event as a separate item - I need the text to wrap, not be elided. Thus a drop-down QTextBrowser.)
Or, for example, I want a drop-down QLabel containing a picture, etc...
Create a QWidgetAction for the drop-down widget, and add it to the tool-button's menu:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
layout = QtGui.QHBoxLayout(self)
self.button = QtGui.QToolButton(self)
self.button.setPopupMode(QtGui.QToolButton.MenuButtonPopup)
self.button.setMenu(QtGui.QMenu(self.button))
self.textBox = QtGui.QTextBrowser(self)
action = QtGui.QWidgetAction(self.button)
action.setDefaultWidget(self.textBox)
self.button.menu().addAction(action)
layout.addWidget(self.button)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(100, 60)
window.show()
sys.exit(app.exec_())
I'm trying to use the QCompleter class for the QLineEdit to give auto completion suggestions while typing, and update the suggestions after the user enters a new text. But when I try to update the Completer with text that starts with something that is already at the completer list, it just crashes the entire app with no visible exception! Even try-except doesn't catch this error, and I can't understand what I am doing wrong...
Below is a simpler example of my code: it is a simple "echo" console application that gets commands from QLineEdit (input text box) and writing it to QTextBrowser (output text box). When entering entirely new "command" (text) it works fine, and being added to the completer, so next time I can see it. But if the new text starts similar to other words in the completer list, choosing it crashes the entire GUI app with no exception visible, not even when I'm running in debug mode...
Please see my example below, and try writing at the upper text box options like: a, aa, aaa (that start similar to completer already word: aaa1)
What am I doing wrong??
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QVBoxLayout, QLineEdit, QTextBrowser, QCompleter
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowTitle('console')
self.setGeometry(10, 50, 500, 800)
# Create text box for input
self.consoleCommandLineEdit = QLineEdit(self)
self.consoleCommandLineEdit.setFixedHeight(25)
self.consoleCommandLineEdit.editingFinished.connect(self.gotConsoleCommand)
self.completerCommands = ['aaa1','aaa2','aaa3'] # initial completer list
completer = QCompleter(self.completerCommands)
self.consoleCommandLineEdit.setCompleter(completer)
# Create text box for output
self.consoleViewer = QTextBrowser(self)
self.consoleViewer.setLineWrapMode(QTextBrowser.NoWrap)
widget = QWidget(self)
self.setCentralWidget(widget)
self.vlay = QVBoxLayout(widget)
self.vlay.addWidget(self.consoleCommandLineEdit)
self.vlay.addWidget(self.consoleViewer)
def gotConsoleCommand(self):
cmd = self.consoleCommandLineEdit.text()
self.consoleCommandLineEdit.setText('')
self.sendCommandToConsole(cmd)
def sendCommandToConsole(self,cmd):
self.consoleViewer.append(cmd) # add cmd to output box
if cmd not in self.completerCommands: # if the command is new, add it to the completer
self.completerCommands.append(cmd) # 1. add the new text to the list we have
completer = QCompleter(self.completerCommands) # 2. create a new completer object
self.consoleCommandLineEdit.setCompleter(completer) # 3. set the new completer as the LineEdit completer
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
I have not yet found the cause of the problem but a better solution than creating a new QCompleter every time you need to add a new text. In this case it is better to use a model to store the texts that is the basis of the QCompleter information.
import sys
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtWidgets import (
QApplication,
QWidget,
QMainWindow,
QVBoxLayout,
QLineEdit,
QTextBrowser,
QCompleter,
)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowTitle("console")
self.setGeometry(10, 50, 500, 800)
# Create text box for input
self.consoleCommandLineEdit = QLineEdit()
self.consoleCommandLineEdit.setFixedHeight(25)
self.consoleCommandLineEdit.editingFinished.connect(self.gotConsoleCommand)
self.model = QStandardItemModel()
self.model.appendRow([QStandardItem(text) for text in ("aaa1", "aaa2", "aaa3")])
completer = QCompleter(self.model, self)
self.consoleCommandLineEdit.setCompleter(completer)
# Create text box for output
self.consoleViewer = QTextBrowser(lineWrapMode=QTextBrowser.NoWrap)
widget = QWidget()
self.setCentralWidget(widget)
vlay = QVBoxLayout(widget)
vlay.addWidget(self.consoleCommandLineEdit)
vlay.addWidget(self.consoleViewer)
def gotConsoleCommand(self):
cmd = self.consoleCommandLineEdit.text()
self.consoleCommandLineEdit.clear()
self.sendCommandToConsole(cmd)
def sendCommandToConsole(self, cmd):
self.consoleViewer.append(cmd) # add cmd to output box
if not self.model.findItems(cmd):
self.model.appendRow(QStandardItem(cmd))
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
Perhaps something that helps us understand the problem is that if you add a parent to QCompleter the problem does not happen: completer = QCompleter(self.completerCommands, self)
but I stress: the best solution is to use a model.
Issue in my code sample:
On "Select..." the selection dialog appears and works basically, but closing it with "Ok" or "Cancel", unfortunately leaves a second window open (title "New Popup"), which is very ugly... :/ I am searching for the way to close that automatically after the user applied the selection in the list, returning to the main window (and the data processing should still work of course).
Thanks for any help! :)
Background: Inside a basic pyqt5 window application, I created a simple list selection as a second (popup) window (separate class), where the user can select an item and the result is then processed in the main window class.
That works basically but I've got an issue with correctly closing the popup window and I was not able to find a solution since hours... Basically I tried self.close, self.destroy, self.hide etc. but nothing had an effect, probably I am missing a piece.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtCore import QTimer
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QLineEdit, QInputDialog, QLabel, QVBoxLayout
class PopupDialog(QtWidgets.QDialog):
def __init__(self):
super(PopupDialog, self).__init__()
self.selected_item = None
layout = QtWidgets.QFormLayout()
self.setLayout(layout)
self.setWindowTitle("New Popup")
self.setMinimumWidth(400)
items = ("C", "C++", "Java", "Python")
item, ok = QInputDialog.getItem(self, "select input dialog",
"list of languages", items, 0, False)
self.selected_item = item
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setMinimumWidth(600)
self.setWindowTitle("Main Window")
self.le = QtWidgets.QLineEdit()
button = QtWidgets.QPushButton("Select...")
button.clicked.connect(self.get_input)
layout = QtWidgets.QHBoxLayout()
layout.addWidget(self.le)
layout.addWidget(button)
self.setLayout(layout)
def get_input(self):
popup = PopupDialog()
popup.exec_()
print("got selection data: ", popup.selected_item)
self.le.setText(popup.selected_item)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Having trouble connecting a QPushButton to a QMessageBox in PyQt5 as there seems to be little documentation in comparison with PyQt4. As it stands the QmessageBox is executing before the main layout I believe this to be an issue somewhere with .self and .exec_()?
The second, and main issue however is concerning connecting the two widgets. I am looking to implement some form of validity check; i.e when both QLineEdit fields contain text then on the click of 'Submit' the form should clear the fields, however if either of the fields are left blank when the 'submit' is clicked, Id like the QMessageBox to be opened. I'm unsure how to implement this as I don't know how to connect both the textField AND the PushButton together.
from PyQt5.QtWidgets import QWidget, QLabel, QLineEdit,QSpinBox,
QDoubleSpinBox, QComboBox, QRadioButton, QPushButton, QHBoxLayout, QVBoxLayout,
QTextEdit, QGridLayout, QApplication, QMessageBox
from PyQt5.QtCore import Qt, pyqtSlot
import csv
class Buttons (QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.Widgets()
self.arrange()
self.close()
self.messageBox()
self.setWindowTitle ("test")
def Widgets(self):
self.nameLabel = QLabel("Name: ")
self.nameLineEdit = QLineEdit()
self.surnameLabel = QLabel("Surname: ")
self.surnameLineEdit = QLineEdit()
self.button1 = QPushButton("submit")
self.button2 = QPushButton("cancel")
self.button1.setMaximumSize(150,20)
self.button2.setMaximumSize(150,20)
def arrange (self):
nameField = QVBoxLayout()
nameField.addWidget(self.nameLabel)
nameField.addWidget(self.nameLineEdit)
nameField.addWidget(self.surnameLabel)
nameField.addWidget(self.surnameLineEdit)
#QHBoxLayout for Buttons:
buttonsLayout = QHBoxLayout()
buttonsLayout.addWidget(self.button1)
buttonsLayout.addWidget(self.button2)
self.button1.setSizePolicy(10,10)
self.button2.setSizePolicy(10,10)
#Creating mainLayout:
mainLayout = QVBoxLayout()
mainLayout.addLayout(nameField)
mainLayout.addLayout(buttonsLayout)
self.setLayout(mainLayout)
#pyqtSlot()
def close(self):
#close window
self.button2.clicked.connect(app.quit)
def clear(self):
pass
#Clear textFields when button is clicked:
#pyqtSlot()
def messageBox(self):
self.message = QMessageBox()
self.message.setText ("submit ERROR")
self.message.setStandardButtons(QMessageBox.Ok)
self.button1.clicked.connect(self.messageBox)
self.message.exec_()
I have tried a few different techniques none of which have worked successfully
self.connect(self.Button1, SIGNAL("clicked()"),self.clicked)
def clicked(self):
QMessageBox.about(self, "message!"(self.messageBox()))
if __name__ == "__main__":
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication (sys.argv)
window = Buttons()
window.show()
sys.exit (app.exec_())
Read over your program and follow the path of execution. Start with window = Buttons() which means the Buttons.__init__ method runs. Within this you run Buttons.messageBox which connects the messageBox slot to the clicked signal of button1. It then opens the message box (self.message.exec_()). Your __init__ method eventually finishes and then app.exec_() is called which actually shows the main GUI and starts the Qt event loop which processes things like button clicks and redraw events.
At this point, your program has already been told to show the message box and is configured to run Buttons.messageBox when you click the button. When you click the button, it shows the message box, and makes an additional connection for the button. Next time you click the button you will get 2 message boxes showing!
The solution is to separate out the signal connection from the messageBox slot. Rather than calling self.messageBox() in your __init__ method, make the signal connection for button1 there.
class Buttons (QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.Widgets()
self.arrange()
self.close()
self.button1.clicked.connect(self.messageBox)
self.setWindowTitle ("test")
#pyqtSlot()
def messageBox(self):
self.message = QMessageBox()
self.message.setText ("submit ERROR")
self.message.setStandardButtons(QMessageBox.Ok)
self.message.exec_()
How do I create a drop-down widget, such as a drop-down QLabel, drop-down QTextBrowser, etc.?
For example, I log information in a QTextBrowser, but I don't want it taking up space on the screen. So I want to be able to click a QToolbutton and have a scrollable QTextBrowser drop-down. (A QComboBox would work too, but I can't just add each event as a separate item - I need the text to wrap, not be elided. Thus a drop-down QTextBrowser.)
Or, for example, I want a drop-down QLabel containing a picture, etc...
Create a QWidgetAction for the drop-down widget, and add it to the tool-button's menu:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
layout = QtGui.QHBoxLayout(self)
self.button = QtGui.QToolButton(self)
self.button.setPopupMode(QtGui.QToolButton.MenuButtonPopup)
self.button.setMenu(QtGui.QMenu(self.button))
self.textBox = QtGui.QTextBrowser(self)
action = QtGui.QWidgetAction(self.button)
action.setDefaultWidget(self.textBox)
self.button.menu().addAction(action)
layout.addWidget(self.button)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(100, 60)
window.show()
sys.exit(app.exec_())