Aligning popup widget in PyQt5 - python

I've seen a number of replies on SO regarding this matter but not specifically to QMenu and QToolButton. Would appreciate some pointers on aligning the popup widget to the right side of the button. Here's a basic code I'm working off..
import sys
from PyQt5.QtWidgets import *
class test(QWidget):
def __init__(self):
super().__init__()
self.resize(200, 100)
layout = QHBoxLayout(self)
label = QLabel('Testing QToolButton Popup')
toolbutton = QToolButton()
toolbutton.setPopupMode(QToolButton.InstantPopup)
widget = QWidget()
widgetLayout = QHBoxLayout(widget)
widgetLabel = QLabel('Popup Text')
widgetSpinbox = QSpinBox()
widgetLayout.addWidget(widgetLabel)
widgetLayout.addWidget(widgetSpinbox)
widgetAction = QWidgetAction(toolbutton)
widgetAction.setDefaultWidget(widget)
widgetMenu = QMenu(toolbutton)
widgetMenu.addAction(widgetAction)
toolbutton.setMenu(widgetMenu)
layout.addWidget(label)
layout.addWidget(toolbutton)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = test()
win.show()
sys.exit(app.exec_())
The outcome looks like this:

The Qt developer thought the default position was correct, so if you want to modify the alignment you must move the QMenu as I show below:
import sys
from PyQt5.QtCore import QPoint
from PyQt5.QtWidgets import (
QApplication,
QHBoxLayout,
QLabel,
QMenu,
QSpinBox,
QToolButton,
QWidgetAction,
QWidget,
)
class Menu(QMenu):
def showEvent(self, event):
if self.isVisible():
button = self.parentWidget()
if button is not None:
pos = button.mapToGlobal(button.rect().bottomRight())
self.move(pos - self.rect().topRight())
super().showEvent(event)
class Test(QWidget):
def __init__(self):
super().__init__()
self.resize(200, 100)
layout = QHBoxLayout(self)
label = QLabel("Testing QToolButton Popup")
toolbutton = QToolButton(popupMode=QToolButton.InstantPopup)
widgetLabel = QLabel("Popup Text")
widgetSpinbox = QSpinBox()
widget = QWidget()
widgetLayout = QHBoxLayout(widget)
widgetLayout.addWidget(widgetLabel)
widgetLayout.addWidget(widgetSpinbox)
widgetAction = QWidgetAction(toolbutton)
widgetAction.setDefaultWidget(widget)
widgetMenu = Menu(toolbutton)
widgetMenu.addAction(widgetAction)
toolbutton.setMenu(widgetMenu)
layout.addWidget(label)
layout.addWidget(toolbutton)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Test()
win.show()
sys.exit(app.exec_())

Related

Get color from variable number of buttons PyQt

I'm trying to get the player's names and the color that they choose for a next step in the project. Getting the names is easy enough but the colors is a bit of a pain.
I don't know how to get the selected color and apply it the button using the QColorDialog. The ultimate goal is to get a structure with player name linked to the color they chose.
Here is what i have:
from PyQt5.QtWidgets import (
QLineEdit,
QWidget,
QApplication,
QLabel,
QMainWindow,
QGridLayout,
QColorDialog,
QPushButton,
QVBoxLayout,
QHBoxLayout,
)
import sys
class NamesPlayers(QMainWindow):
""" name screen, ask the names of the players """
def __init__(self, nb_players):
super().__init__()
self.layout_widget = QWidget()
self.player_names = []
self.player_colors = []
main_layout = QVBoxLayout()
names_layout = QGridLayout()
button_layout = QHBoxLayout()
button_list = []
for i in range(nb_players):
label = QLabel("Name :")
player_name = QLineEdit()
color_button = QPushButton("Color")
color_button.setStyleSheet("background-color: white")
names_layout.addWidget(label, i, 0)
names_layout.addWidget(player_name, i, 1)
names_layout.addWidget(color_button, i, 2)
button_list.append(color_button)
self.player_names.append(player_name)
self.player_colors.append(color_button.styleSheet())
self.confirm_button = QPushButton("Confirm")
button_layout.addWidget(self.confirm_button)
main_layout.addLayout(names_layout)
main_layout.addLayout(button_layout)
for button in button_list:
button.clicked.connect(self.open_colordialog)
self.layout_widget.setLayout(main_layout)
self.setCentralWidget(self.layout_widget)
def open_colordialog(self):
color_dialog = QColorDialog()
color_dialog.exec_()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = NamesPlayers(4)
window.show()
app.exec_()
My main issue i guess is the fact that the number of buttons for the color can vary and when I click on those buttons, the address for the QColorDialog is always the same which is not what I want.
Any help is appreciated
One possible solution is to use the sender() method to get the button pressed:
import sys
from PyQt5 import QtCore, QtWidgets
class NamesPlayers(QtWidgets.QMainWindow):
""" name screen, ask the names of the players """
def __init__(self, nb_players, parent=None):
super().__init__(parent)
names_layout = QtWidgets.QGridLayout()
for i in range(nb_players):
label = QtWidgets.QLabel("Name :")
player_name = QtWidgets.QLineEdit()
color_button = QtWidgets.QPushButton("Color")
color_button.setStyleSheet("background-color: white")
color_button.clicked.connect(self.open_colordialog)
names_layout.addWidget(label, i, 0)
names_layout.addWidget(player_name, i, 1)
names_layout.addWidget(color_button, i, 2)
self.confirm_button = QtWidgets.QPushButton("Confirm")
central_widget = QtWidgets.QWidget()
main_layout = QtWidgets.QVBoxLayout(central_widget)
button_layout = QtWidgets.QHBoxLayout()
button_layout.addWidget(self.confirm_button)
main_layout.addLayout(names_layout)
main_layout.addLayout(button_layout)
self.setCentralWidget(central_widget)
#QtCore.pyqtSlot()
def open_colordialog(self):
button = self.sender()
color_dialog = QtWidgets.QColorDialog()
if color_dialog.exec_() == QtWidgets.QColorDialog.Accepted:
button.setStyleSheet(
"background-color: {}".format(color_dialog.selectedColor().name())
)
button.clearFocus()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = NamesPlayers(4)
window.show()
sys.exit(app.exec_())
Another possible solution is to send the button pressed (with the methods indicated in this answer):
from functools import partial
# ...
for i in range(nb_players):
# ...
color_button = QtWidgets.QPushButton("Color")
color_button.setStyleSheet("background-color: white")
color_button.clicked.connect(partial(self.open_colordialog, color_button))
# or
# color_button.clicked.connect(lambda *args, btn=color_button: self.open_colordialog(btn))
def open_colordialog(self, button):
color_dialog = QtWidgets.QColorDialog()
if color_dialog.exec_() == QtWidgets.QColorDialog.Accepted:
button.setStyleSheet(
"background-color: {}".format(color_dialog.selectedColor().name())
)
button.clearFocus()

Custom method on click ClearButton in QLineEdit

Is it possible to run a custom method on clicking the ClearButton in a QLineEdit?
For instance i have a ClearButton inside a QCombobox and i want to set a default-index on clicking the ClearButton in this ComboBox.
I have already tried to subclass the 'clear()' slot of the QLineEdit, but without success.
qtawesome
import sys
from PyQt5.QtWidgets import (QLineEdit, QApplication, QWidget, QVBoxLayout)
import qtawesome as qt
class Widget(QWidget):
def __init__(self, parent=None):
super(QWidget, self).__init__(parent)
self.flag = 0
self.layout = QVBoxLayout()
self.line_edit = QLineEdit()
self.line_edit.setClearButtonEnabled(False)
self.line_edit.textChanged.connect(self._on_line_edit_text_changed)
self.clear_icon = qt.icon('mdi.delete-circle-outline', color='gray', color_active='black')
self.clear_action = None
self.layout.addWidget(self.line_edit)
self.setLayout(self.layout)
def _on_line_edit_text_changed(self):
if self.line_edit and self.line_edit.text():
if not self.clear_action:
self.clear_action = self.line_edit.addAction(self.clear_icon, QLineEdit.TrailingPosition)
self.clear_action.triggered.connect(self._on_clear_clicked)
elif self.clear_action and self.line_edit and not self.line_edit.text():
self.line_edit.removeAction(self.clear_action)
self.clear_action = None
def _on_clear_clicked(self):
self.line_edit.clear()
if __name__ == "__main__":
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

How do I print in multiple QLabel when I click the PushButton?

How do I print in multiple QLabel when I click the PushButton?, because it only works in Quantity I also want it in Item Name and Price. I tried putting multiple print_click(self) it wont work it say redefinition of unused 'print_clink'. Thanks in advance
My Code:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QPushButton, QLabel, QLineEdit
from PyQt5.QtCore import pyqtSlot`
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.ItemName = QLabel('Item Name:')
self.Item_Line = QLabel('')
self.Item_Name = QLineEdit('')
self.PriceName = QLabel('Price:')
self.Price_Line = QLabel('')
self.Price_Name = QLineEdit('')
self.QuantityName = QLabel('Quantity:')
self.Quantity_Line = QLabel('0')
self.Quantity_Name = QLineEdit()
self.Update_button = QPushButton("Update")
self.Update_button.clicked.connect(self.print_click)
self.Clear_button = QPushButton("Clear")
self.Clear_button.clicked.connect(self.clear_click)
hbox = QHBoxLayout(self)
hbox.addWidget(self.ItemName)
hbox.addWidget(self.Item_Name)
hbox.addWidget(self.PriceName)
hbox.addWidget(self.Price_Name)
hbox.addWidget(self.QuantityName)
hbox.addWidget(self.Quantity_Line)
hbox.addWidget(self.Quantity_Name)
hbox.addWidget(self.Update_button)
hbox.addWidget(self.Clear_button)
self.show()
self.Clear_button.clicked.connect(self.Item_Line.clear)
self.Clear_button.clicked.connect(self.Item_Name.clear)
self.Clear_button.clicked.connect(self.Price_Line.clear)
self.Clear_button.clicked.connect(self.Price_Name.clear)
self.Clear_button.clicked.connect(self.Quantity_Line.clear)
self.Clear_button.clicked.connect(self.Quantity_Name.clear)
#pyqtSlot()
def print_click(self):
self.Quantity_Line.setText(self.Quantity_Name.text())
def clear_click(self):
self.Quantity_Line(self.Quantity_Name.text(''))
return self.Quantity
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())
I'm not completely sure of the expected result but I guess there are some mistakes and redundancies in your code :
the Price_Line and Item_Line weren't added to the QHBoxLayout
the method print_click wasn't setting the text from Price_Name and Item_Name on the respective Price_Line and Item_Line.
the clear_click method wasn't really useful as you already connected the clear method of every other elements on that button.
The following code is adapted from yours, paying attention to the points mentioned above :
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QPushButton, QLabel, QLineEdit
from PyQt5.QtCore import pyqtSlot
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.ItemName = QLabel('Item Name:')
self.Item_Line = QLabel('')
self.Item_Name = QLineEdit()
self.PriceName = QLabel('Price:')
self.Price_Line = QLabel('')
self.Price_Name = QLineEdit()
self.QuantityName = QLabel('Quantity:')
self.Quantity_Line = QLabel('0')
self.Quantity_Name = QLineEdit()
self.Update_button = QPushButton("Update")
self.Update_button.clicked.connect(self.print_click)
self.Clear_button = QPushButton("Clear")
hbox = QHBoxLayout(self)
hbox.addWidget(self.ItemName)
hbox.addWidget(self.Item_Line)
hbox.addWidget(self.Item_Name)
hbox.addWidget(self.PriceName)
hbox.addWidget(self.Price_Line)
hbox.addWidget(self.Price_Name)
hbox.addWidget(self.QuantityName)
hbox.addWidget(self.Quantity_Line)
hbox.addWidget(self.Quantity_Name)
hbox.addWidget(self.Update_button)
hbox.addWidget(self.Clear_button)
self.show()
self.Clear_button.clicked.connect(self.Item_Line.clear)
self.Clear_button.clicked.connect(self.Item_Name.clear)
self.Clear_button.clicked.connect(self.Price_Line.clear)
self.Clear_button.clicked.connect(self.Price_Name.clear)
self.Clear_button.clicked.connect(self.Quantity_Line.clear)
self.Clear_button.clicked.connect(self.Quantity_Name.clear)
def print_click(self):
self.Price_Line.setText(self.Price_Name.text())
self.Item_Line.setText(self.Item_Name.text())
self.Quantity_Line.setText(self.Quantity_Name.text())
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())

pyqt5 Is there a limit to loading widgets using clicked.connect?

I'm using the QPushButton to load the UI. First -> Jumin -> Department -> next -> next I want to create the UI in order. The problem is that I can not load the third Department into the QMainwindow window. I do not know why
When you create a widget in QVBoxLayout, it changes the size of the widget according to the wallpaper like wxpython layout (wx.all). Can not change the position (move) and size (resize) by automatic centering?
import sys
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.center_widget = QWidget()
self.setCentralWidget(self.center_widget)
self.FirstUI()
def FirstUI(self):
self.btn1 = QPushButton('test1', self)
self.btn1.move(50, 50)
self.btn1.clicked.connect(self.btn1_click)
def JuminUI(self):
self.ju1 = QLineEdit('13')
self.btn2 = QPushButton('^^^^^^^^^^')
self.ju_text = QLabel('asd')
self.jumim_layout = QVBoxLayout()
self.jumim_layout.addWidget(self.ju_text)
self.jumim_layout.addWidget(self.ju1)
self.jumim_layout.addWidget(self.btn2)
self.centralWidget().setLayout(self.jumim_layout)
self.btn2.clicked.connect(self.btn2_click)
def DepartmentUI(self):
self.depart_layout = QVBoxLayout()
self.depart_layout.addWidget(QPushButton('sdfsdf'))
self.centralWidget().setLayout(self.depart_layout)
def btn1_click(self):
self.btn1.deleteLater()
self.JuminUI()
def btn2_click(self):
self.ju1.deleteLater()
self.btn2.deleteLater()
self.ju_text.deleteLater()
self.DepartmentUI()
if __name__ == "__main__":
app = QApplication(sys.argv)
fream = MainWindow()
fream.show()
app.exec_()
creating and removing widgets is almost always a bad idea, and your code falls into those bad ideas, it's always best to hide the widgets and for that you should use the QStackedWidget, what QStackedWidget does is just make a widget visible on all widgets that you have been assigned by changing the currentIndex.
import sys
from functools import partial
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.center_widget = QtWidgets.QStackedWidget()
self.setCentralWidget(self.center_widget)
self.FirstUI()
self.JuminUI()
self.DepartmentUI()
def FirstUI(self):
widget = QtWidgets.QWidget()
self.btn1 = QtWidgets.QPushButton('test1', widget)
self.btn1.move(50, 50)
self.center_widget.addWidget(widget)
self.btn1.clicked.connect(partial(self.center_widget.setCurrentIndex, 1))
def JuminUI(self):
widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(widget)
self.ju1 = QtWidgets.QLineEdit('13')
self.btn2 = QtWidgets.QPushButton('^^^^^^^^^^')
self.ju_text = QtWidgets.QLabel('asd')
lay.addWidget(self.ju_text)
lay.addWidget(self.ju1)
lay.addWidget(self.btn2)
self.center_widget.addWidget(widget)
self.btn2.clicked.connect(partial(self.center_widget.setCurrentIndex, 2))
def DepartmentUI(self):
widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(widget)
lay.addWidget(QtWidgets.QPushButton('sdfsdf'))
self.center_widget.addWidget(widget)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
fream = MainWindow()
fream.show()
sys.exit(app.exec_())

PyQt: How to create a scrollable window

I think it should be much easier to create a scrollable window in PyQt.
I have a list of labels that goes out of the window and I would like to scroll down to view them. At the moment the code does not give me an error, but the window just doesn't appear:
class Example(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
lbl_arr = makeLabelArr()
for i in range(1,8):
qb = lbl_arr[i]
# qb.setFixedWidth(300)
layout.addWidget(qb)
layout.setAlignment(Qt.AlignTop)
scroll = QScrollArea()
scroll.setWidget(self)
scroll.setWidgetResizable(True)
scroll.setFixedHeight(400)
layout.addWidget(scroll)
self.setLayout(layout)
self.setGeometry(0, 0, 600, 220)
self.setWindowTitle('SnP watchlist')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
#print(QDesktopWidget().availableGeometry())
ex = Example()
sys.exit(app.exec_())
Make the window itself a QScrollArea, like this:
class Window(QScrollArea):
def __init__(self):
super(Window, self).__init__()
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setAlignment(Qt.AlignTop)
for index in range(100):
layout.addWidget(QLabel('Label %02d' % index))
self.setWidget(widget)
self.setWidgetResizable(True)
There is an example here: https://www.learnpyqt.com/tutorials/qscrollarea/
from PyQt5.QtWidgets import (QWidget, QSlider, QLineEdit, QLabel, QPushButton, QScrollArea,QApplication,
QHBoxLayout, QVBoxLayout, QMainWindow)
from PyQt5.QtCore import Qt, QSize
from PyQt5 import QtWidgets, uic
import sys
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.scroll = QScrollArea() # Scroll Area which contains the widgets, set as the centralWidget
self.widget = QWidget() # Widget that contains the collection of Vertical Box
self.vbox = QVBoxLayout() # The Vertical Box that contains the Horizontal Boxes of labels and buttons
for i in range(1,50):
object = QLabel("TextLabel: "+str(i))
self.vbox.addWidget(object)
self.widget.setLayout(self.vbox)
#Scroll Area Properties
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scroll.setWidgetResizable(True)
self.scroll.setWidget(self.widget)
self.setCentralWidget(self.scroll)
self.setGeometry(600, 100, 1000, 900)
self.setWindowTitle('Scroll Area Demonstration')
self.show()
return
def main():
app = QtWidgets.QApplication(sys.argv)
main = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You should set layout after adding the scroll bar widget.
class Example(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
lbl_arr = makeArrayOfLabelsHTML()
for i in range(1,8):
qb = lbl_arr[i]
layout.addWidget(qb)
layout.setAlignment(Qt.AlignTop)
scroll = QScrollArea()
scroll.setWidget(self)
scroll.setWidgetResizable(True)
scroll.setFixedHeight(400)
layout.addWidget(scroll)
# set layout after adding scroll bar
self.setLayout(layout)
self.setGeometry(0, 0, 600, 220)
self.setWindowTitle('SnP watchlist')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
#print(QDesktopWidget().availableGeometry())
ex = Example()
sys.exit(app.exec_())

Categories

Resources