pyqt5 drop down list with sub lists - python

I am trying to figure out how I can create a drop down list (currently using QComoboBox), with a few items, and when I hover my mouse/click on one of the options, it shows a sub list, containing a few more options.
I tried to search online but couldn't find anything that I could use.
Here an example of what I want to achieve (sorry for terrible quality, but I think it makes it clear what my goal is..)
Does any one have an idea of how to make a sub list inside a list?
Edit
After trying #eyllanesc suggestion I still having a problem:
I am using two files: one file that contains all the objects like buttons and such, and the other one that contains some functions and basically makes the GUI functional.
I defined this on my first file
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(751, 650)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.menuBtn=QtWidgets.QPushButton(self.centralwidget)
self.menuBtn.setGeometry(QtCore.QRect(100, 220, 100, 100))
self.productMenu=QtWidgets.QMenu(self.centralwidget)
self.menu1=self.productMenu.addMenu("options")
self.menu1.addAction("option 1")
self.menu2=self.productMenu.addMenu("option 2")
self.menu2.addAction("option 2a")
self.menu2.addAction("option 2b")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Test"))
self.menuBtn.setText(_translate("MainWindow", "Menu"))
And on the second file (the functional) I wrote this:
from PyQt5 import QtWidgets, QtCore, QtGui
from stackTest import Ui_MainWindow
import sys
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.productMenu.triggered.connect(lambda action: self.ui.menuBtn.setText(action.text("Hello!")))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
application = ApplicationWindow()
application.show()
sys.exit(app.exec_())
When I run the code, I can see the button, but when I click it nothing happens.
How can I make it run so when I push the button I'll get the menu.

One possible solution is to use a button (QPushButton, QToolButton, etc.) and establish a QMenu as I show below:
import sys
from PyQt5 import QtCore, QtWidgets
def create_menu(d, menu):
if isinstance(d, list):
for e in d:
create_menu(e, menu)
elif isinstance(d, dict):
for k, v in d.items():
sub_menu = QtWidgets.QMenu(k, menu)
menu.addMenu(sub_menu)
create_menu(v, sub_menu)
else:
action = menu.addAction(d)
action.setIconVisibleInMenu(False)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
d = ["1", "2", "3", {"4": ["5", "6", {"7": ["8", "9"]}]}, {"10": "11"}]
menu = QtWidgets.QMenu(self)
create_menu(d, menu)
button = QtWidgets.QPushButton()
button.setMenu(menu)
menu.triggered.connect(lambda action: button.setText(action.text()))
lay = QtWidgets.QHBoxLayout(self)
lay.addWidget(button)
lay.addStretch()
self.resize(640, 480)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
The code provided by the OP must add the QMenu to the QPushButton:
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
from stackTest import Ui_MainWindow
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.menuBtn.setMenu(self.ui.productMenu)
self.ui.productMenu.triggered.connect(
lambda action: self.ui.menuBtn.setText(action.text())
)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
application = ApplicationWindow()
application.show()
sys.exit(app.exec_())

Related

PyQt5 : QMovie is truncated even after resizing

I am adding a gif to my QMainWindow. I want the size of my window to match exactly the size of QMovie but my QMovie is truncated at the first place and even if use resize on that, its still not showing fully.
Here is the code :
from typing import Text
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
import sys
class MainWindow(qtw.QDialog):
def __init__(self, *arg, **kwargs):
super().__init__(*arg, **kwargs)
self.centralwidget = qtw.QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.label = qtw.QLabel(self.centralwidget)
movie = qtg.QMovie('mic_listen2.gif')
movie.setScaledSize(qtc.QSize(50,50))
self.label.setMovie(movie)
movie.start()
self.show()
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
w = MainWindow()
sys.exit(app.exec_())
Here is the gif
https://media.giphy.com/media/QGMXK7Byy6MSXKVRlc/giphy.gif
Here is my output
You must set both the minimum size of the label and that of the widget.
#!/usr/bin/env python3
from typing import Text
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui
import sys
class MainWindow(QtWidgets.QDialog):
def __init__(self, movsize=50, *arg, **kwargs):
super().__init__(*arg, **kwargs)
self.centralwidget = QtWidgets.QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.movie = QtGui.QMovie("giphy.gif")
self.movie.setScaledSize(QtCore.QSize(movsize, movsize))
self.movie.start()
self.label.setMovie(self.movie)
self.label.setMinimumSize(self.movie.scaledSize())
self.centralwidget.setMinimumSize(self.label.minimumSize())
# Uncomment to force the window to take the size of the movie.
# self.setMaximumSize(self.movie.scaledSize())
self.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w2 = MainWindow(movsize=160)
w3 = MainWindow(movsize=500)
sys.exit(app.exec_())

Not able to print clicked line in PyQt5

I am using QTextBrowser for printing data but want to check which line is clicked and get its string. I looked at other resources and was able to make this, but its not printing the line.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class TextBrowser(QtWidgets.QTextBrowser):
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.MouseButtonPress:
tc = self.cursorForPosition(event.pos())
print("text = ", tc.block().text())
return super().eventFilter(source, event)
def setupUi(self, MainWindow):
MainWindow.resize(104, 105)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName(u"gridLayout_8")
self.code_textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
self.gridLayout.addWidget(self.code_textBrowser, 1, 0, 1, 1)
self.code_textBrowser.viewport().installEventFilter(self)
self.code_textBrowser.append("ab\ncde\nfghi\njklmn\nopqrstu")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
MainWindow.setMenuBar(self.menubar)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = TextBrowser()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
One of the reasons why I do not recommend modifying the code generated by .ui is that it causes confusion so for my possible solution you must restore that file (run pyuic again) and call the generated file as gui.py.
The problem is that the cursorForPosition returns the cursor associated with "TextBrowser" (in your case "ui") and not with "code_textBrowser", that a class inherits from class T does not imply that all attributes are of the same class T they will be the same.
The solution is to implement the logic in another class that inherits from a QObject like QMainWindow and implement the logic using code_textBrowser.
import sys
from PyQt5.QtCore import QEvent
from PyQt5.QtWidgets import QApplication, QMainWindow
from gui import Ui_MainWindow
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.code_textBrowser.viewport().installEventFilter(self)
def eventFilter(self, obj, event):
if (
obj is self.ui.code_textBrowser.viewport()
and event.type() == QEvent.MouseButtonPress
):
tc = self.ui.code_textBrowser.cursorForPosition(event.pos())
print("text = ", tc.block().text())
return super().eventFilter(obj, event)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

QTextEdit clickable text

I have a log I have created with a simple [Add] text near each line which I want to be able to press and have it run a function that will know which line/text it has in this line.
I can also use QTextBrowser if needed for this.
My code:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(401, 308)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.chat_log = QtWidgets.QTextEdit(self.centralwidget)
self.chat_log.setGeometry(QtCore.QRect(10, 10, 381, 241))
self.chat_log.setReadOnly(True)
self.chat_log.setObjectName("chat_log")
MainWindow.setCentralWidget(self.centralwidget)
class MainFrame(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainFrame, self).__init__(parent)
self.setupUi(self)
def appending(self):
self.chat_log.append("Somethingsomething [Add]")
self.chat_log.append("Hello[Add]")
self.chat_log.append("What is up [Add]")
self.chat_log.append("Big boy [Add]")
if __name__ == "__main__":
app = QApplication(sys.argv)
form = MainFrame()
form.show()
form.appending()
app.exec_()
What I want is basically when someone pressed "[Add]" it will know which line it pressed and will print the text in that line
Say I pressed the first line it will then print("Somethingsomething")
You need to make your [Add] texts clickable, the easiest way to do so is using HTML markup, then you just need to identify the text you had been clicking on:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class MyTextEdit(QtWidgets.QTextEdit):
def mousePressEvent(self, e):
self.link = self.anchorAt(e.pos())
def mouseReleaseEvent(self, e):
if self.link:
print(f"Clicked on {self.link}")
self.link = None
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(401, 308)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.chat_log = MyTextEdit(self.centralwidget)
self.chat_log.setGeometry(QtCore.QRect(10, 10, 381, 241))
self.chat_log.setReadOnly(True)
self.chat_log.setObjectName("chat_log")
MainWindow.setCentralWidget(self.centralwidget)
class MainFrame(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainFrame, self).__init__(parent)
self.setupUi(self)
def appending(self):
messages = ["Somethingsomething", "Hello", "What is up", "Big bo"]
for msg in messages:
self.chat_log.append(
f'<span>{msg}<a style="color: pink" href="{msg}">[Add]</a></span>'
)
if __name__ == "__main__":
app = QApplication(sys.argv)
form = MainFrame()
form.show()
form.appending()
app.exec_()
Out:
Clicked on Big bo
Clicked on Hello
Clicked on Somethingsomething

PyQt5 - not able to add elements to QTabWidget

I am trying to add a second tab (containing a pie chart visualization) to my PyQt5 GUI, but I cannot figure out how to display it. This second tab is laid out in a separate class and my program has the following structure:
main.py
from PyQt5 import QtWidgets, QtCore
from gui import UiMainWindow
import sys
class Logic(QtWidgets.QMainWindow, UiMainWindow,):
def __init__(self):
super().__init__()
self.setupUi(self)
self.treeView = QtWidgets.QTreeView(self.tabwidget.main_tab)
self.treeView.setGeometry(QtCore.QRect(270, 90, 801, 571))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
logic = Logic()
logic.show()
sys.exit(app.exec_())
gui.py
from PyQt5 import QtWidgets, QtCore
from main_tab import MainTab
class UiMainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("Invoice Manager")
MainWindow.resize(1120, 750)
MainWindow.setTabShape(QtWidgets.QTabWidget.Rounded)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.tabwidget = MainTab(self.centralwidget)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
self.tabwidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("Test", "Test"))
self.tabwidget.setTabText(
self.tabwidget.indexOf(self.tabwidget.main_tab),
_translate("MainWindow", "Main"))
self.tabwidget.setTabText(
self.tabwidget.indexOf(self.tabwidget.visual_tab),
_translate("MainWindow", "Tab_2"))
main_tab.py
from PyQt5 import QtCore, QtWidgets
from tab_2 import Tab2
class MainTab(QtWidgets.QTabWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setGeometry(QtCore.QRect(0, 0, 1120, 750))
self.main_tab = QtWidgets.QWidget()
self.addTab(self.main_tab, "")
self.visual_tab = Tab2()
self.addTab(self.visual_tab, "")
tab_2.py
from PyQt5 import QtWidgets
from PyQt5.QtChart import QChart, QChartView, QPieSeries, QPieSlice
from PyQt5.QtGui import QPainter, QPen
from PyQt5.QtCore import Qt
class Tab2(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.layout = QtWidgets.QVBoxLayout()
self.setLayout(self.layout)
self.create_piechart()
def create_piechart(self):
series = QPieSeries()
series.append("Label 1", 2)
series.append("Label 2", 2)
series.append("Label 3", 2)
pie_slice = QPieSlice()
pie_slice.setExploded(True)
pie_slice.setLabelVisible(True)
pie_slice.setPen(QPen(Qt.darkGreen, 2))
pie_slice.setBrush(Qt.green)
chart = QChart()
chart.legend().hide()
chart.addSeries(series)
chart.createDefaultAxes()
chart.setAnimationOptions(QChart.SeriesAnimations)
chart.setTitle("Pie Chart Example")
chart.legend().setVisible(True)
chart.legend().setAlignment(Qt.AlignBottom)
chartview = QChartView(chart)
chartview.setRenderHint(QPainter.Antialiasing)
I have been trying various solutions, but no luck so far. Any help would be highly appreciated!

Change QLabel text dynamically not working

I am trying to change the QLabel text dymanically using QtDesigner, pyqt5.
Below is the code i am trying to use for changing the QLabel text dymanically.
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label, 0, QtCore.Qt.AlignHCenter)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-size:28pt;\">" + self.getTime() + "</span></p></body></html>"))
def getTime(self):
time = QTime.currentTime().toString()
return time
def data(self):
time = QTime.currentTime().toString()
print("Time: " + time)
self.label.setText(time)
return time
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ex = Ui_MainWindow()
timer = QtCore.QTimer()
timer.timeout.connect(ex.data)
timer.start(1000) # 1 Second Refesh Rate
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
When i tried to run the code in QtDesigner the output window open's for a second and closed automatically. Not sure what is causing the output window to close. Please advise me to resolve the issue.
I recommend you execute your code in the terminal/CMD since many IDEs do not handle the Qt errors, if you do it you would get the following error:
Traceback (most recent call last):
File "main.py", line 33, in data
self.label.setText(time)
AttributeError: 'Ui_MainWindow' object has no attribute 'label
The error indicates that label does not exist, and that is correct because label is created after calling setupUi() but in your case "ex" does not call it. A possible solution is to first create the window and then start the timer:
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
timer = QtCore.QTimer()
timer.timeout.connect(ui.data)
timer.start(1000) # 1 Second Refesh Rate
sys.exit(app.exec_())
But a better solution is to follow the recommendations of PyQt(1) that states that you should not modify the class provided by Qt but use it as the widget interface:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label, 0, QtCore.Qt.AlignHCenter)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(
_translate(
"MainWindow",
'<html><head/><body><p><span style=" font-size:28pt;"></span></p></body></html>',
)
)
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
timer = QtCore.QTimer(self)
timer.timeout.connect(self.data)
timer.start(1000)
self.data()
def data(self):
time_str = QTime.currentTime().toString()
self.label.setText(
'<html><head/><body><p><span style=" font-size:28pt;">{}</span></p></body></html>'.format(
time_str
)
)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
(1) Using the Generated Code
The problem is that you instatiate Ui_MainWindow twice: first as ex and later as ui. You connect the timer.timeout to ex, but call setupUi and show only for ui.
Try this:
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
timer = QtCore.QTimer()
timer.timeout.connect(ui.data)
timer.start(1000) # 1 Second Refesh Rate
sys.exit(app.exec_())
Qt Designer is only used for designing the UI, it does not allow running the program. Qt Creator can run programs, but only "real" Qt C++ programs.
You need to launch your .py scripts outside of Qt Designer, as if it were a normal Python script.

Categories

Resources