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

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.

Related

Menubar sometimes does not become un-greyed when QFileDialog closes

OS: W10. This may be significant. If you have different results on a different platform, feedback would be helpful.
Here is an MRE. If you run it and go Ctrl+O, the menu labels become greyed. If you select a file in the QFileDialog by clicking the "Open" button or using its mnemonic (Alt+O), the open-file dialog is dismissed and the "Files" and "Help" menus become un-greyed.
However, if you go Ctrl+O again, and this time enter the name of a file in the "File name" box (QLineEdit), and then press Return, the dialog is dismissed (with a successful selection result) but the "Files" and "Help" menus remain greyed-out. It looks like this:
import sys, os
from PyQt5 import QtWidgets, QtCore, QtGui
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Greying of menus MRE')
self.setGeometry(QtCore.QRect(100, 100, 400, 200))
menubar = QtWidgets.QMenuBar(self)
self.setMenuBar(menubar)
self.files_menu = QtWidgets.QMenu('&Files', self)
menubar.addMenu(self.files_menu)
self.help_menu = QtWidgets.QMenu('&Help', self)
menubar.addMenu(self.help_menu)
self.new_action = QtWidgets.QAction('&New', self)
self.files_menu.addAction(self.new_action)
self.open_action = QtWidgets.QAction('&Open', self)
self.files_menu.addAction(self.open_action)
self.open_action.setShortcut("Ctrl+O")
self.open_action.triggered.connect(self.open_file)
def focusInEvent(self, event ):
print('main_window focusInEvent')
super().focusInEvent(event)
def focusOutEvent(self, event ):
print('main_window focusOutEvent')
super().focusInEvent(event)
def activateWindow(self):
print('main_window activateWindow')
super().activateWindow()
def open_file(self):
print('open file')
main_window_self = self
# open_doc_dialog = QtWidgets.QFileDialog(self.get_main_window())
class OpenDocFileDialog(QtWidgets.QFileDialog):
def accepted(self):
print('accepted')
super().accepted()
def accept(self):
print('accept')
super().accept()
def close(self):
print('close')
super().close()
def done(self, r):
print(f'done r {r}')
# neither of these solves the problem:
# main_window_self.activateWindow()
# main_window_self.files_menu.activateWindow()
super().done(r)
def hide(self):
print(f'hide')
super().hide()
def focusInEvent(self, event ):
print('focusInEvent')
super().focusInEvent(event)
def focusOutEvent(self, event ):
print('focusOutEvent')
super().focusInEvent(event)
def activateWindow(self):
print('activateWindow')
super().activateWindow()
open_doc_dialog = OpenDocFileDialog(self)
open_doc_dialog.setWindowTitle('Choose file')
open_doc_dialog.setDirectory(os.getcwd())
# we cannot use the native dialog, because we need control over the UI
options = open_doc_dialog.Options(open_doc_dialog.DontUseNativeDialog)
open_doc_dialog.setOptions(options)
open_doc_button = open_doc_dialog.findChild(QtWidgets.QDialogButtonBox).button(QtWidgets.QDialogButtonBox.Open)
lineEdit = open_doc_dialog.findChild(QtWidgets.QLineEdit)
# this does not solve the problem
# lineEdit.returnPressed.disconnect()
# lineEdit.returnPressed.connect(open_doc_button.click)
print(f'open_doc_button {open_doc_button}, lineEdit {lineEdit}')
# show the dialog
dialog_code = open_doc_dialog.exec()
if dialog_code != QtWidgets.QDialog.Accepted: return
sel_files = open_doc_dialog.selectedFiles()
print(f'sel_files: {sel_files}')
app = QtWidgets.QApplication([])
main_window = MainWindow()
main_window.show()
sys.exit(app.exec())
This problem can be understood, if not solved, with reference to this answer.
Note that this greying-out is not disablement. As explained in the above link, this has to do with "active/inactive states" of the menus (or their labels). The menus remain enabled throughout, although in this case it's impossible to know that while the open-file dialog is showing because it is modal. Clicking on one menu after the dialog has gone, or just hovering over it, is enough to un-grey them both...
The explanation, as I understand it, is that the "File name" box QLineEdit has a signal, returnPressed, which appears to activate something subtley different to the slot which is invoked when you use the "Choose" button. You can see I have experimented with trying to re-wire that signal, to no avail.
The method done of the QFileDialog appears to be called however the dialog closes (unlike close!), so I tried "activating" the main window... and then the individual QMenus... Doesn't work.
I am not clear how to get a handle on this "active state" business or why the slot connected to returnPressed is (seemingly) unable to give the "active state" back to the menus when the other slot manages to do so.
Edit
Searching on Musicamante's "unpolishing" suggestion led me to this:
lineEdit.returnPressed.disconnect()
def return_pressed():
style = main_window_self.menubar.style()
style.unpolish(main_window_self.menubar)
open_doc_button.click()
lineEdit.returnPressed.connect(return_pressed)
... unfortunately this doesn't work.
This looks like a possible Windows-related bug, since I can't reproduce it on Linux. As a work-around, you could try forcing a repaint after the dialog closes:
# show the dialog
dialog_code = open_doc_dialog.exec()
self.menubar.repaint()
Finally got it, thanks to Musicamante's suggestion:
lineEdit.returnPressed.disconnect()
def return_pressed():
style = main_window_self.menubar.style()
style.unpolish(main_window_self.menubar)
open_doc_button.click()
main_window_self.menubar.repaint()
lineEdit.returnPressed.connect(return_pressed)
... I actually tried this several times, just to make sure it was doing what was intended. So in fact, fortunately, no single-shot timer was needed in this case.

PyQt5 QWidget event blocks other events. How to avoid it?

I have the next problem:
A created a custom widget (simple derived QWidget class). When I middle-mouse click on it - it creates another QWidget class (sort of context menu). When I release middle-mouse button - that context widget disappears. So that works. What does not work is - that context widget also has some content added, like other small widgets, icons, etc and they all have their own custom events (simple example - enterEvent and leaveEvent with prints indicating those events). But those inner widget events are not working, they are blocked while I keep middle-mouse pressed. When I release it - context widget disappears. Would like to know if there is any solution to let inner widgets'events work as expected.
Here is a minimal example where inner widget does not run mouse events as they are blocked by MainWidget event:
from PyQt5 import QtWidgets, QtGui, QtCore
class SomeSmallWidget(QtWidgets.QWidget):
def __init__(self, increment=1, globalValue=0):
super(SomeSmallWidget, self).__init__()
# UI
self.setMinimumWidth(40)
self.setMaximumWidth(40)
self.setMinimumHeight(40)
self.setMaximumHeight(40)
self.mainLayout = QtWidgets.QVBoxLayout()
self.setLayout(self.mainLayout)
def enterEvent(self, event):
print('Entered') # NOT WORKING
super(SomeSmallWidget, self).enterEvent(event)
def leaveEvent(self, event):
print('Leaved') # NOT WORKING
super(SomeSmallWidget, self).leaveEvent(event)
class ContextWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ContextWidget, self).__init__()
self.setMouseTracking(True)
# position
point = parent.rect().topLeft()
global_point = parent.mapToGlobal(point)
self.move(global_point - QtCore.QPoint(0, 0))
self.innerWidget = SomeSmallWidget() # Here is that small widget, which event does not work
self.mainLayout = QtWidgets.QVBoxLayout()
self.setLayout(self.mainLayout)
self.mainLayout.addWidget(self.innerWidget)
class MainWidget(QtWidgets.QLineEdit):
def __init__(self, value='0'):
super(MainWidget, self).__init__()
self.setMouseTracking(True)
self.popMenu = None
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.MiddleButton: # while we keep MMB pressed - we see context widget
self.popMenu = ContextWidget(parent=self)
self.popMenu.show()
super(MainWidget, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == QtCore.Qt.MiddleButton:
if self.popMenu:
self.popMenu = None
Events are not blocked, all mouse events are sent to widget that was under cursor when mouse button was pressed. Usually (always) it makes more sense. Imagine two buttons next to each other. Suppose user pressed one, moved cursor and released over second button. What was his intention? He probably changed his mind. If button triggers action on mouse press - this options will not be available and it's probably too soon, if button triggers action on mouse release, which button should recieve mouserelease event? If we send mouseevent to second button - that was not pressed it will trigger action that used didn't want. If we dont send mouserelease event to first button - it will stay in sunken mode. Imagine user is selecting text in lineedit, and while selecting he leaves lineedit and goes to other widgets, should they react somehow, and should focus be switched? Probably not. So there is only one active window and only one focused widget at a time and it receives keyboard and mouse input and reacts to it. Most of menus are shown after mouserelease and closes on next mouseclick, providing better user experience.
However, If you still want your widget to receive mouse events, you can achive this by translating it from ContextWidget to SomeSmallWidget like this:
class SomeSmallWidget(QtWidgets.QWidget):
...
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.fillRect(self.rect(), QtCore.Qt.blue)
def onMouseEnter(self):
print('onMouseEnter')
def onMouseLeave(self):
print('onMouseLeave')
class MainWidget(QtWidgets.QLineEdit):
...
def mouseMoveEvent(self, event):
if self.popMenu:
self.popMenu.mouseTest(event.globalPos())
class ContextWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
...
self._inside = False
def mouseTest(self, p):
widget = self.innerWidget
rect = QtCore.QRect(widget.mapToGlobal(QtCore.QPoint(0,0)), widget.size())
inside = rect.contains(p)
if inside != self._inside:
if inside:
widget.onMouseEnter()
else:
widget.onMouseLeave()
self._inside = inside
Notice I added paintEvent to see the widget bounds.

LineEdit box in PyQt5

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)

How to choose line-edit according to which button was clicked

I have two buttons (Eg. A andB) which do the same things (based on user selection). So when you select something, then click the button, the selection's name will be input into the line-edit for the button. For example, if I click on buttonA, the input will be to lineEditA.
Currently I have created a signal function as follows:
def _connections_setup(self):
self.btnA.clicked.connect(self.get_sel_nameA)
self.btnB.clicked.connect(self.get_sel_nameB)
def get_sel_nameA(self):
sel_name = get_name_from_sel()
self.line_editA.setText(sel_name)
def get_sel_nameB(self):
sel_name = get_name_from_sel()
self.line_editA.setText(sel_name)
"""
def get_sel_name(self):
# Returns me a blank
button = self.sender()
print button.objectName()
# My objective here would be, if btnA is clicked, the sel_name will be inputted into lineEditA. Likewise for btnB
"""
Instead of creating two similar functions, how can I determine which button was clicked and have the selection's name to be input correctly into the line-edit?
I tried using self.sender() (see get_sel_name()) but it does not seems to return me the button name.
The sender() function only works in slots directly connected to signals. So your code needs to look something like this:
def _connections_setup(self):
self.btnA.clicked.connect(self.get_sel_name)
self.btnB.clicked.connect(self.get_sel_name)
def get_sel_name(self):
button = self.sender()
name = button.objectName()
if button is self.btnA:
self.line_editA.setText(name)
elif button is self.btnB:
self.line_editB.setText(name)

Getting Information Which QLabel Clicked in PyQT

I have a list of QLabel and want to learn which QLabel clicked . When i look for the making QLabel clickable , this code has worked :
labels[i].mousePressEvent = self.print_some
def print_some(self, event):
print("Clicked")
But I didn't figure out which object clicked . How can i do that ?
You can easily make custom receivers for events, which would contain the event source information:
import functools
labels[i].mousePressEvent = functools.partial(self.print_some, source_object=labels[i])
def print_some(self, event, source_object=None):
print("Clicked, from", source_object)

Categories