LineEdit box in PyQt5 - python

When I press the Key Enter in the lineEdit box performs both function enter_LineEdit() and function click_Edit(). Why does it perform function click_Edit()? It must not!
I would like someone to explain to me why does it work this way?
#!/usr/bin/python3.6
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QMainWindow,QApplication,QPushButton,QDialog,QHBoxLayout,QLabel,QWidget,QLineEdit
from PyQt5 import QtGui
from PyQt5 import QtCore
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(100,100,600,400)
self.CreateBtn()
self.show()
def CreateBtn(self):
button = QPushButton("Second Window", self)
button.setGeometry(QtCore.QRect(30,100,200,80))
button.setIconSize(QtCore.QSize(70,70))
button.clicked.connect(self.SecWin)
def SecWin(self):
self.d = SecondWindow()
self.d.Create_SecWin()
self.d.Create_Object()
self.d.Create_Layout()
class SecondWindow(QDialog):
def Create_SecWin(self):
self.setGeometry(600,360,400,100)
self.show()
def Create_Object(self):
self.btnEdit = QPushButton("Edit",self)
self.btnEdit.clicked.connect(self.click_Edit)
self.labelSearch = QLabel("Search:",self)
self.lineEdit = QLineEdit(self)
self.lineEdit.returnPressed.connect(self.enter_LineEdit)
def Create_Layout(self):
hbox1 = QHBoxLayout()
hbox1.addWidget(self.btnEdit)
hbox1.addWidget(self.labelSearch)
hbox1.addWidget(self.lineEdit)
self.setLayout(hbox1)
def click_Edit(self):
print("Philip")
def enter_LineEdit(self):
print("Karl")
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec_())

If you review the docs of the autoDefault property of QPushButton:
This property holds whether the push button is an auto default button
If this property is set to true then the push button is an auto
default button.
In some GUI styles a default button is drawn with an extra frame
around it, up to 3 pixels or more. Qt automatically keeps this space
free around auto-default buttons, i.e., auto-default buttons may have
a slightly larger size hint.
This property's default is true for buttons that have a QDialog
parent; otherwise it defaults to false.
See the default property for details of how default and auto-default
interact.
And also from the default property:
[...]
A button with this property set to true (i.e., the dialog's default
button,) will automatically be pressed when the user presses enter,
with one exception: if an autoDefault button currently has focus, the
autoDefault button is pressed. When the dialog has autoDefault buttons
but no default button, pressing enter will press either the
autoDefault button that currently has focus, or if no button has
focus, the next autoDefault button in the focus chain.
[...]
That is, some of the QPushButtons will be pressed when the enter key is pressed in a QDialog since all QPushButtons have the autoDefault property in True, so the solution is to set it to False:
self.btnEdit = QPushButton("Edit", self)
self.btnEdit.setAutoDefault(False)

Related

PyQt5 QCompleter how to clear the QLineEdit after auto-completion text was selected

How can I clear a text box (QLineEdit) after an auto completion text was selected using QCompleter? - Nothing is working for me.
Below is a simpler example of my code: it is a simple "echo" console application that gets text commands from QLineEdit (input text box) and writing it to QTextBrowser (output text box). Once the user pressed ENTER on the input text box, the text from that one should be added to the output text box, and the input text box should be cleared (to be ready for the next command).
When entering a new text, that is not in the auto completion options, all works well: the text is added to the output text box and cleared from the input text box. However when the user chooses one of the options from the auto completion options (using the down/up arrows) and then press ENTER, the text is being added to the output text box, but is not removed from the input text box...
Both cases call my callback function (trigger function) which should clear the input text box, using:
self.consoleCommandLineEdit.clear()
but when the auto completion text is chosen by ENTER, the input text box is not cleared...
The text is added correctly to the output text box, so my callback function is called properly.
It looks like that the Completer is filling my text box after I clear it...
How can I disable this behavior and make sure the input text box is cleared? can I remove it??
Below is the entire example. You can try that by typing 'zzz' or any other text that is not in the auto completions list and press ENTER, and then try typing 'a' then go down with the keyboard to 'aaa1' (or any other on the auto completion options) and press ENTER. You will see the input text box is not cleared in this case.
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('aaa1'))
self.model.appendRow(QStandardItem('aaa2'))
self.model.appendRow(QStandardItem('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.consoleViewer.append(cmd) # add cmd to output box
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
The problem is that the task of the QCompleter is to complete the text of the QLineEdit and that is what it is doing, I explain in detail what is happening: The user selects one of the popup options of the QCompleter, then press the enter key, This causes the popup to close but also sends the event of the enter key to the QLineEdit causing the editingFinished signal to be emitted, your code deletes the text but then the QCompleter adds the selected text.
The solution is that after adding that text you have to clean using a QTimer and the activated QCompleter signal:
# ...
completer = QCompleter(self.model, self)
completer.activated.connect(self.onActivated)
# ...
def onActivated(self):
QTimer.singleShot(0, self.consoleCommandLineEdit.clear)

How to work with the "?" (what's this widget) on the title bar of a PyQT Dialog

On the right of the title bar of a PyQt QDialog (see below, next to the "x") there is a "?" that is supposed to help the user query help for any other widget on the Dialog window.
What should I do (programmatically) to get it to work. Once the "?" isClicked, one should be able to capture the next widget clicked and provide a ToolTip or something like that. In PyQt, I do not know how to capture the isClicked event on the "?".
I have seen a couple of posts where the question was how to make the "?" disappear, but the discussion there uses Qt, not PyQt, so I do not understand it, and they are not talking about what I need. I need to make it work as intended. See How can I hide/delete the "?" help button on the "title bar" of a Qt Dialog? and PyQt4 QInputDialog and QMessageBox window flags
You can set the whatsThis property to any widget you want:
self.someWidget.setWhatsThis('hello!')
From that point on, whenever you click on the "?" button and then click on that widget, a tooltip with that text will be shown.
Since the "what's this" mode is individually set to widgets, there's no easy way to capture it globally (as far as I know of) because if the widget has no whatsthis property set that feature won't be available for it.
Also, whenever you enter the "what's this" mode, the cursor will probably change according to the contents of the whatsthis property: if it's not set, the cursor will probably show a "disabled" icon.
I've created a basic workaround for this issue, which automatically enables any child widget's whatsthis (if none is already set) whenever the mode is activated: as soon as the EnterWhatsThisMode is fired, it automatically installs a custom object that acts as an event filter, and emits a signal if the whatsthis event is called; as soon as the mode exits, the filter is removed.
I used a separate object for the event filter because there's no way to know what event filter have been already installed to a widget, and if you already installed the parent's one, removing it automatically would be an issue.
class WhatsThisWatcher(QtCore.QObject):
whatsThisRequest = QtCore.pyqtSignal(QtWidgets.QWidget)
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.WhatsThis:
self.whatsThisRequest.emit(source)
return super(WhatsThisWatcher, self).eventFilter(source, event)
class W(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
layout = QtWidgets.QVBoxLayout(self)
hasWhatsThisButton = QtWidgets.QPushButton('Has whatsThis')
layout.addWidget(hasWhatsThisButton)
hasWhatsThisButton.setWhatsThis('I am a button!')
noWhatsThisButton = QtWidgets.QPushButton('No whatsThis')
layout.addWidget(noWhatsThisButton)
self.whatsThisWatchedWidgets = []
self.whatsThisWatcher = WhatsThisWatcher()
self.whatsThisWatcher.whatsThisRequest.connect(self.showCustomWhatsThis)
whatsThisButton = QtWidgets.QPushButton('Set "What\'s this" mode')
layout.addWidget(whatsThisButton)
whatsThisButton.clicked.connect(QtWidgets.QWhatsThis.enterWhatsThisMode)
def event(self, event):
if event.type() == QtCore.QEvent.EnterWhatsThisMode:
for widget in self.findChildren(QtWidgets.QWidget):
if not widget.whatsThis():
# install the custom filter
widget.installEventFilter(self.whatsThisWatcher)
# set an arbitrary string to ensure that the "whatsThis" is
# enabled and the cursor is correctly set
widget.setWhatsThis('whatever')
self.whatsThisWatchedWidgets.append(widget)
elif event.type() == QtCore.QEvent.LeaveWhatsThisMode:
while self.whatsThisWatchedWidgets:
widget = self.whatsThisWatchedWidgets.pop()
# reset the whatsThis string to none and uninstall the filter
widget.setWhatsThis('')
widget.removeEventFilter(self.whatsThisWatcher)
return super(W, self).event(event)
def showCustomWhatsThis(self, widget):
widgetPos = widget.mapTo(self, QtCore.QPoint())
QtWidgets.QWhatsThis.showText(
QtGui.QCursor.pos(),
'There is no "what\'s this" for {} widget at coords {}, {}'.format(
widget.__class__.__name__, widgetPos.x(), widgetPos.y()),
widget)
A couple of notes about this:
I used a button to activate the whatsthis mode, as on my window manager on Linux there's no window title button for that;
Some widgets may contain subwidgets, and you'll get those instead of the "main" one (the most common case are QAbstractScrollArea descendands, like QTextEdit or QGraphicsView, which might return the viewport, the inner "widget" or the scrollbars);
By default the task of that button is to enable whatsThis: press "?", then press the widget and you will see the message associated with whatsThis property.
If you want to add other actions(open url, add QToolTip, etc) you can monitor the QEvent::EnterWhatsThisMode and QEvent::LeaveWhatsThisMode events overriding the event() method or using an eventFilter().
from PyQt5 import QtCore, QtGui, QtWidgets
class Dialog(QtWidgets.QDialog):
def event(self, event):
if event.type() == QtCore.QEvent.EnterWhatsThisMode:
print("enter")
QtGui.QDesktopServices.openUrl(QtCore.QUrl("foo_url"))
elif event.type() == QtCore.QEvent.LeaveWhatsThisMode:
print("leave")
return super().event(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Dialog()
w.setWhatsThis("Whats this")
w.setWindowFlags(
QtCore.Qt.WindowContextHelpButtonHint | QtCore.Qt.WindowCloseButtonHint
)
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

Why does a QLineEdit widget generate an "editingFinished" signal when other widgets are activated?

I'm trying to understand why QLineEdit "editingFinished" signals are generated when other widgets are selected. In the example below the "on_lineedit" method is called when the combo box is selected. Why?
import sys
from PyQt5 import QtWidgets
class MyApp(QtWidgets.QDialog):
def __init__(self, *args):
super().__init__(*args)
# create combobox:
combobox = QtWidgets.QComboBox(self)
combobox.addItems(['Item 1', 'Item 2'])
# create line edit
lineedit = QtWidgets.QLineEdit(self)
lineedit.editingFinished.connect(self.on_lineedit)
# layout:
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget( combobox )
vbox.addWidget( lineedit )
self.setLayout(vbox)
def on_lineedit(self):
print('on_lineedit')
app = QtWidgets.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
I know that this issue can be avoided by connecting the "textChanged" signal instead of the "editingFinished" signal like this:
lineedit.textChanged.connect(self.on_lineedit)
and I've seen similar issues raised elsewhere (links below) but I still don't understand why the "editingFinished" signal is generated when the combobox is selected.
Qt qspinbox editingFinished signal on value changed
Suppress QLineEdit editingFinished signal when certain button is clicked
From http://doc.qt.io/archives/qt-4.8/qlineedit.html#editingFinished
This signal is emitted when the Return or Enter key is pressed or the line edit loses focus.
The signal is emitted because it is designed to be. The other widget your click on is not really relevant here, what is relevant is that it the line edit loses focus and it is that which causes the signal to be emitted. Clicking on another widget is just one of many ways your line edit might lose focus.

PySide how to tell if a push button has been clicked?

I have a layout with 5 buttons which I act as "menus", so you click on one button and one view will show up, you click another button and another view shows up. I need to find out which button is clicked so I can do something based on which button is pressed. Something like
if button1_is_clicked:
do_something()
else:
do_something_else()
What would be the best way to approach this?
Here is my code:
I want to be able to change the stylesheet of the button, so an active state and a non-active state
from PySide import QtCore
from PySide import QtGui
import VulcanGui
#--------------------------------------------------------------------------
class Program(QtGui.QMainWindow, VulcanGui.Ui_MainWindow):
def __init__(self, parent=None):
""" Initialize and setup the User Interface """
super(Program, self).__init__(parent)
self.setupUi(self)
""" Populate the Main Area """
self.mainArea.setHtml(self.intro_text())
""" Button Signal/Slots """
self.introButton.toggled.connect(self.intro_area)
self.runVulcanButton.clicked.connect(self.vulcan_run_area)
self.vulcanLogButton.clicked.connect(self.vulcan_log_area)
self.hostFileButton.clicked.connect(self.edit_host_area)
self.configEditButton.clicked.connect(self.edit_config_area)
def intro_text(self):
content_file = open("../content/intro_text.html").read()
return content_file
'''
Get the content to print
'''
def intro_area(self):
content_file = open("../content/intro_text.html").read()
self.mainArea.setHtml(content_file)
'''
Function that will display the data when the 'Run Vulcan' button is pressed
'''
def vulcan_run_area(self):
self.mainArea.setPlainText("Button Two ")
'''
Function that will display the data when the 'Vulcan Log' button is pressed
'''
def vulcan_log_area(self):
self.mainArea.setPlainText("Button Three")
'''
Function that will display the data when the 'Edit Host File' button is pressed
'''
def edit_host_area(self):
self.mainArea.setPlainText("Button Four")
'''
Function that will display the data when the 'Edit Config File' button is pressed
'''
def edit_config_area(self):
self.mainArea.setPlainText("Button Five")
#--------------------------------------------------------------------------
if __name__ == "__main__":
import sys
program = QtGui.QApplication(sys.argv)
mWindow = Program()
mWindow.show()
sys.exit(program.exec_())
I suggest you learn the basics of Qt to get acquainted with signals and slots.
You need to make the initially visible QPushButtons checkable (otherwise the 'revealed' buttons will only appear whilst the button is held down), and connect the toggled(bool) signal to the setVisible(bool) slot of the buttons you want to 'reveal'. Obviously for the buttons that are initially invisible, you would have to call setVisible(false) upon instantiation.
There are other, more reusable, ways of achieving the same effect - but this will get you started.

uncheck radiobutton - PyQt4

In this sample of code:
from PyQt4.QtGui import QDialog, QPushButton, QRadioButton, QHBoxLayout, QApplication, QButtonGroup
import sys
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent=None)
button = QPushButton('Button')
self.radiobutton1 = QRadioButton('1')
self.radiobutton2 = QRadioButton('2')
#self.group = QButtonGroup()
#self.group.addButton(self.radiobutton1)
#self.group.addButton(self.radiobutton2)
#self.group.setExclusive(False)
layout = QHBoxLayout()
layout.addWidget(button)
layout.addWidget(self.radiobutton1)
layout.addWidget(self.radiobutton2)
self.setLayout(layout)
button.clicked.connect(self.my_method)
def my_method(self):
self.radiobutton1.setChecked(False)
self.radiobutton2.setChecked(False)
app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
When the button clicked I expect the selected radioButton to be unchecked, but that never happens. If I uncomment the comment lines and run the code, then I can uncheck radioButtons. But another problem occurs. Because the group is not exclusive, I can set both radioButtons checked something that must not happens.
What should I do to be able to unckeck the buttons while only one button at a time can be selected?
This feels like cheating, but it works:
import sys
import PyQt4.QtGui as QtGui
class Form(QtGui.QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
button = QtGui.QPushButton('Button')
button.clicked.connect(self.my_method)
self.radiobutton1 = QtGui.QRadioButton('1')
self.radiobutton2 = QtGui.QRadioButton('2')
layout = QtGui.QHBoxLayout()
layout.addWidget(button)
layout.addWidget(self.radiobutton1)
layout.addWidget(self.radiobutton2)
self.setLayout(layout)
self.group = QtGui.QButtonGroup()
self.group.addButton(self.radiobutton1)
self.group.addButton(self.radiobutton2)
def my_method(self):
self.group.setExclusive(False)
self.radiobutton1.setChecked(False)
self.radiobutton2.setChecked(False)
self.group.setExclusive(True)
app = QtGui.QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
As you've pointed out, when self.group.setExclusive(False) is set, you can untoggle both radio buttons.
And when self.group.setExclusive(True), only one radio button can be set.
So my_method simply calls self.group.setExclusive(False) so it can unset both radio buttons, then resets self.group.setExclusive(True).
PS. I think parent should not be set to None on this line:
super(Form, self).__init__(parent = None)
since if a non-trivial parent is sent to Form, you would probably want to pass that parent on to QDialog.__init__.
To anyone looking for a simple fix to this very annoying problem, connect each button to a slot that controls the CheckState of the other buttons.
Simply add the list of buttons you want to a QButtonGroup, get the list of buttons, check that the sender is not the same button, and uncheck others.
Assuming that you instantiate your buttons in a loop, you can easily implement this:
self.bg = QButtonGroup()
self.bg.setExclusive(False)
for button in list_of_buttons:
self.bg.addButton(button)
button.clicked.connect(self.uncheck_other_buttons)
def uncheck_other_btns(self):
for button in self.bg.buttons(): # returns the list of all added buttons
if self.sender() != button: # in PyQt5, button.objectName() fails if name isn't set,
# instead, simply check that the signal sender() object
# is not the same object as the clicked button
button.setChecked(False) # then set all other buttons to be unchecked

Categories