how to find parent TabPage of widget in pyqt5? - python

I found code for finding child widget <Top-widget.findChildren(widget-type)>, but I couldn't able to find command for getting parent widget.
I created QTabWidget with multiple tabs . in each tabs I have some widgets.
I want to highlight tab page of given widget element.
I created one code to achieve this. and it is working. but I feels this is not straight forward. can you suggest better solution to achieve the same?
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
def color_parent_tab(widget,Tab):
tabs={Tab.widget(i):i for i in range(Tab.count())}
while widget!=None:
try:
if widget in tabs:
Tab.tabBar().setTabTextColor(tabs[widget], QColor('red'))
widget=widget.parent()
except Exception as e:print(e)
if __name__ == '__main__':
app = QApplication([])
window=QTabWidget()
w=QWidget()
VL=QVBoxLayout(w)
window.addTab(w,'tab1')
L=QLabel('Label1')
VL.addWidget(L)
L2=QLabel('Label2')
VL.addWidget(L2)
w=QWidget()
VL=QVBoxLayout(w)
window.addTab(w,'tab2')
L3=QLabel('Label1')
VL.addWidget(L3)
L4=QLineEdit()
VL.addWidget(L4)
color_parent_tab(L3,window)
window.showMaximized()
app.exec()

You can use isAncestorOf, which returns True if the widget in the argument is a child (or grandchild, at any level):
def color_parent_tab(widget, Tab):
for i in range(Tab.count()):
if Tab.widget(i).isAncestorOf(widget):
Tab.tabBar().setTabTextColor(i, QColor('red'))
return

Related

Adding items to QComboBox

I'm trying to add items to two combo boxes.
The code below runs with no errors, I see the list I'm trying to add and "fin" is printed to the terminal, but the combo boxes are showing up empty.
from PyQt5.QtWidgets import QMainWindow
from PyQt5 import QtWidgets
# import GUI from designer file
from main_menu import Ui_main_menu
# import other functions
from add_functions import ChangeLists
class Main(QMainWindow, Ui_main_menu):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.setupUi(self)
self.init_lists()
def init_lists(self):
# Team List
team_list_file = open(r'C:\NHLdb_pyqt\files\NHLteams.txt', 'r')
team_list = team_list_file.read().splitlines()
team_list_file.close()
print("team list: ", team_list)
# Initial Player List
player_list_init = "Please Select a Team"
# Populate combo box lists
self.team_select_combobox.addItems(team_list)
self.player_select_combobox.addItem(player_list_init)
# connect combo box to function that will change player list based on team list selection
# self.team_select_combobox.currentTextChanged.connect(ChangeLists.team_changed)
print("fin")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
main_menu = QtWidgets.QDialog()
ui = Main()
ui.setupUi(main_menu)
# main_menu = Main()
main_menu.show()
sys.exit(app.exec_())
You're using two methods of loading the ui at the same time, the multiple inheritance approach and the "direct approach", and you're actually showing the main_menu instance of QDialog (which doesn't have any init_lists function).
The result is that even if the init_lists is called, it's "shown" (actually, not) in the wrong window, since you're showing the main_menu instance.
Clearly, you should not use both of them, as the first is enough (and usually the most used/suggested), and then show the correct instance object:
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
main_menu = Main()
main_menu.show()
sys.exit(app.exec_())
Do note that there's something else that is wrong with your implementation: you're inheriting from QMainWindow in the Main class, but later you're trying to set up the ui using a QDialog.
Only the base class created in Designer should be used (it doesn't matter what approach you use to load the ui). I can assume that the ui was created using a QDialog (otherwise an exception would have occurred, as a QMainWindow ui would try to use setCentralWidget() which is a function that doesn't exist for QDialog).
So, you either create a new main window in Designer and copy your existing layout in that (if you need the features of a QMainWindow, such as a menu bar, a status bar, dock widgets or toolbars), or you correctly use the QDialog class in the constructor:
class Main(QDialog, Ui_main_menu):
# ...

Problems with connect in pyqt5

I have a problem. I am writing a simple app in Pyqt5. I am trying to do this block of code in PyQt:
QNetworkAccessManager manager;
QNetworkReply *response = manager.get(QNetworkRequest(QUrl(url)));
QEventLoop event;
connect(response,SIGNAL(finished()),&event,SLOT(quit()));
event.exec();
QString html = response->readAll();
But when I am trying to use "connect" IDE tells me that "MainWindow" don't have method. How can I do it ?? Please help
This is my code:
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent = None):
super(MainWindow, self).__init__()
# window settings
self.setWindowTitle("Hello world app")
# main layout
self.lay = QtWidgets.QVBoxLayout()
# main widgets
self.label = QtWidgets.QLabel("Enter URL:")
self.line = QtWidgets.QLineEdit()
self.label_conn = QtWidgets.QLabel("")
self.btn = QtWidgets.QPushButton("Connect")
self.btn.clicked.connect(self.btn_click)
# adding widgets to layout
self.lay.addWidget(self.label, alignment=QtCore.Qt.AlignBottom)
self.lay.addWidget(self.line)
self.lay.addWidget(self.btn)
self.lay.addWidget(self.label_conn, alignment=QtCore.Qt.AlignTop | QtCore.Qt.AlignCenter)
self.setLayout(self.lay)
self.connect()
The connect method belongs to the signal that you wish to connect to a specific slot, not to the MainWindow widget itself. (BTW, you should consider inheriting from QMainWindow instead.)
In your code, the MainWindow widget is not a signal, so does not have a connect method. Also, even if it did, you need to specify the slot to which you're trying to connect the signal, which is also missing.
In other words, you must declare a pyqtSignal, if you're not using a pre-existing one, and then connect it to the pyqtSlot of your choice. Whether this slot is pre-defined or a custom one is up to you.
Consider the following code snippet, which I tested in Python3:
#!/usr/bin/python3 -B
import sys
from PyQt5.QtWidgets import QApplication, QDialog, QPushButton
if __name__ == '__main__':
app = QApplication(sys.argv)
diag = QDialog()
diag.setWindowTitle('Signal Demo')
diag.resize(200,50)
btn = QPushButton(diag)
btn.setText('Close Dialog')
# connect button's clicked signal to dialog's close slot
btn.clicked.connect(diag.close)
diag.show()
diag.exec_()
Notice that the button's clicked signal, not the button, is what gets connected to the dialog's close slot, not the dialog itself.
EDIT 1:
Just noticed that the very code you've posted has an example of how to properly perform a connection.
If your code has not simply been copy-pasted from some other place, you should've noticed that you seem to know how to properly connect signals and slots already. This line plainly gives it away:
self.btn.clicked.connect(self.btn_click)
If your MainWindow does have a btn_click method, then it should get invoked after the QPushButton named btn gets clicked.
EDIT 2:
Based on your recent comment, you seem to simply be trying to translate a snippet for a larger application, so consider the following code:
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from PyQt5.QtCore import QEventLoop, QUrl
app = QApplication(sys.argv)
url = 'https://stackoverflow.com'
manager = QNetworkAccessManager()
response = manager.get(QNetworkRequest(QUrl(url)))
event = QEventLoop()
response.finished.connect(event.quit)
event.exec()
html = str(response.readAll()) # in Python3 all strings are unicode, so QString is not defined
print(html)
The code above was tested to work as expected.
PS: I did notice that some seemingly valid URLs were returning an empty response (e.g. http://sourceforge.net/), but others, such as the one above, worked fine. It seems to be unrelated to the code snippet itself.

QtDesigner in combination with PyQt (more specific adding a button to a tab created in QtDesigner

I'm quite new to PyQt and QtDesigner so probably it's easy what I'm tring to do but I couldn't find a working example anywhere.
I've created a GUI in QtDesigner with a tabWidget and multiple tabs (which are QWidgets) named tab,tab_2 etc.
Now I'm trying to add a pushbutton for example to the first tab (called tab). My previous try created the button in a new window..
What is the correct way to do this?
import sys
from PyQt5 import QtWidgets,QtCore, QtGui,uic
from PyQt5.Qt import QPushButton
class Main(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = uic.loadUi('example.ui',self)
self.ui.tab.btn1=QtWidgets.QPushButton('buttonn')
self.ui.tab.btn1.show()
if __name__ == '__main__':
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
window=Main()
window.show()
sys.exit(app.exec_())
The normal way to use QTabWidget is to do the following: Create a
QTabWidget. Create a QWidget for each of the pages in the tab dialog,
but do not specify parent widgets for them. Insert child widgets into
the page widget, using layouts to position them as normal. Call
addTab() or insertTab() to put the page widgets into the tab widget,
giving each tab a suitable label with an optional keyboard shortcut.
Try this:
class Main(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = uic.loadUi('example.ui',self)
btn1=QtWidgets.QPushButton('buttonn')
self.ui.tabwidget.addTab(btn1, 'Tab name')
If you have the tabs already created, just create the button as a child of the widget inside the tab:
btn1=QtWidgets.QPushButton('buttonn', self.ui.tab)

PyQt: ListWidget.insertItem not shown

I have a fairly simply PyQt question. (Python 3.4, PyQt 4.11.3, Qt 4.8.5) I built a very simple dialog using Qt Designer (Ui_Dialog). This object has a QPushButton, a QLineEdit, and a QListWidget. I wrote another object that inherits from Ui_Dialog, and sets up a returnPressed signal from QLineEdit that should add some text to the QListWidget. Unfortunately, this does not work.
Here's my code:
import sys
from PyQt4 import QtGui
from dialog import Ui_Dialog
class ImDialog(QtGui.QDialog, Ui_Dialog):
def __init__(self):
super(ImDialog, self).__init__()
self.setupUi(self)
self.lineEdit.returnPressed.connect(self.additem)
self.pushButton.clicked.connect(self.listWidget.clear)
def additem(self):
text = self.lineEdit.text()
print(text)
self.listWidget.insertItem(0, text)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
ui = ImDialog()
ui.show()
sys.exit(app.exec_())
The text in the line editor prints fine to the terminal, but it is not added to the listWidget.
Interestingly, if I comment out the sys.exit line and run this in an IPython terminal, I can add as much text as I like to the listWidget without a problem.
[In 1]: %run that_program.py
[In 2]: ui.listWidget.insertItem(0, "Test") # This works fine
If anyone has any suggestions to get this to work (outside IPython), I would appreciate the help. Thanks
There is only one button in your dialog, and so it will become the auto-default. This means that whenever you press enter in the dialog, the button will receive a press event, even if it doesn't currently have the keyboard focus.
So the item does get added to the list-widget - it's just that it then immediately gets cleared by the auto-default button.
To fix this, reset the auto-default like so:
self.pushButton.setAutoDefault(False)
(NB: you can also change this property in Qt Designer).

QStatusBar message disappears on menu hover

I have a very basic QMainWindow application that contains a menubar and a statusbar. When I hover over the menu the status message disappears. More precisely, the status message is cleared. I have no idea what is causing this behavior but it's resulting in a very difficult workaround for what I hoped to be trivial behavior.
This is problematic for the following reason:
I can make the message permanent by adding a QLabel widget to the QStatusBar, but then I get the awkward border. I don't want the border. The only way I know how to remove the border is via QStatusBar.setStyleSheet(). I am using a palette for my color scheme as opposed to a stylesheet so modifying the stylesheet messes up other colors. I also can't restore the original statusBar QLabel color when I make a modification via the stylesheet. I'm not the best at using stylesheets.
Is there a way to prevent the menu interaction from clearing the status message? If not, is there a way to remove the border from the StatusBar when adding a QLabel widget while preserving my palette (maybe not via stylesheets)?
#!/usr/bin/env python
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class win(QMainWindow):
def __init__(self,parent=None):
super(win,self).__init__(parent)
self.menubar = QMenuBar(self)
self.fileMenu = QMenu("File")
self.exitAction = QAction("Exit",self)
self.fileMenu.addAction(self.exitAction)
self.menubar.addMenu(self.fileMenu)
self.statusBar().showMessage("Hello")
self.connect(self.exitAction,SIGNAL("triggered()"), self.close)
if __name__ == "__main__":
app = QApplication(sys.argv)
GUI = win()
GUI.show()
app.exec_()
I got the same problem, and I found another way which is creating a new QLabel
self.myMessage = QtGui.QLabel()
self.myMessage.setText("Hello")
and add it as an widget to the status bar on the left
self.statusBar.addWidget(self.myMessage)
or on the right
self.statusBar.addPermanentWidget(self.myMessage)
Basically, each widget you hover over sets the status bar text to their statusTip property even when that property is an empty string.
For QMenu, the text is stored in the menuAction action status tip, so, you can have a text instead of just clearing the status bar with something like this:
self.fileMenu.menuAction().setStatusTip("File Menu is hovered")
To prevent anything to change the status bar, you can probably install an eventFilter on the status bar and filter out all QStatusTipEvent.
Just to update Lazywii's answer regarding using a QLabel. That code didn't work exactly as is so maybe there have been some changes since 2016 but what did work in 2020 on PyQt5 is:
self.myMessage = QtWidgets.QLabel()
self.myMessage.setText("My message not affected by tooltips from hovering")
self.statusbar.addWidget(self.myMessage)
One complete example
# Created by BaiJiFeiLong#gmail.com at 2022/2/15 22:27
from PySide2 import QtWidgets, QtCore, QtGui
class StatusTipFilter(QtCore.QObject):
def eventFilter(self, watched: QtCore.QObject, event: QtCore.QEvent) -> bool:
if isinstance(event, QtGui.QStatusTipEvent):
return True
return super().eventFilter(watched, event)
app = QtWidgets.QApplication()
window = QtWidgets.QMainWindow()
window.menuBar().addMenu("File")
window.statusBar().showMessage("Ready")
window.menuBar().installEventFilter(StatusTipFilter(window))
window.show()
app.exec_()
And to answer the portion about removing the border from the statusbar: self.statusbar().setStyleSheet("QStatusBar::item{ border: 0px solid black };") does the trick. It is important to setStyleSheet only on the statusbar object and not the entire application.

Categories

Resources