PyQt - Fixed title for combobox - pyqt

I want to create a PyQt combobox with a fixed title. More specific this means that I want to have a dropdown menu from which the user can select but the dropdown button is always labeled the same. So for example I want to create an option for the user to specify where the legend of a plot is drawn. The button for this should always be labled "Legend" but when you click on it, it opens a dropdown menu with the placing options such as "upper right", "upper left", "top", etc. Once the user selected an option the legend is updated but the button still sais "Legend".
I have this so far:
self.fnLegendButton = QtGui.QComboBox()
self.fnLegendButton.addItems('Upper right,Lower right,Upper left,Lower left,Top,Disable'.split(','))
self.fnLegendButton.setCurrentIndex(0)
self.fnLegendButton.setToolTip('Select the legend position.')
self.fnLegendButton.currentIndexChanged.connect( <positioning function> )
self.fnLegendButton.setMaximumWidth(60)

Here's a working example:
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.fnLegendButton = QtGui.QComboBox(self)
self.fnLegendButton.addItems(
'Legend,Upper right,Lower right,Upper left,Lower left,Top,Disable'.split(','))
self.fnLegendButton.setCurrentIndex(0)
self.fnLegendButton.setToolTip('Select the legend position.')
self.fnLegendButton.currentIndexChanged[
str].connect(self.avoid_db_change)
self.fnLegendButton.setMaximumWidth(100)
self.fnLegendButton.move(50, 50)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('QtGui.QCheckBox')
self.show()
def avoid_db_change(self, text):
print("Processing {0} item".format(text))
self.fnLegendButton.blockSignals(True)
self.fnLegendButton.setCurrentIndex(0)
self.fnLegendButton.blockSignals(False)
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The important bits of this code are inside the avoid_db_change, that function is the one used to keep the "Legend" text no matter which item you've pressed. Now, you don't want to fire that function again when self.fnLegendButton.setCurrentIndex(0) is executed, so to avoid that, you surround it by a couple of blockSignals methods. Just try to comment the blockSignals methods and you'll understand what this means.

Related

how to display a textlabel as well as draw a circle on clicking a pushbutton "ADD"

I have read all related article of multiple slots with one signal but I am unable to display at the time of drawing a circle both trigerred by a push button "ADD". I can display the text label near the circle before clicking the button but i want it to dislay only after clicking the button. Please Help. Also, i want the text label to be near circle and can be modified anytime on clicking
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow,QPushButton,QWidget
from PyQt5 import QtGui
from PyQt5.QtCore import QRect,Qt
from PyQt5.QtGui import QPainter,QBrush, QPen
from PyQt5 import QtCore
class Window(QMainWindow):
def __init__(self):
super(Window,self).__init__()
title="layout management"
left=500
top=200
width=500
height=400
iconName="fosseeicon.jpg"
self.setWindowTitle(title)
self.setWindowIcon(QtGui.QIcon(iconName))
self.setGeometry(left, top, width, height)
self.should_paint_circle = False
self.windowcomponents()
self.initUI()
self.show()
def initUI(self):
if self.should_paint_circle:
self.label=QtWidgets.QLabel(self)
self.label.setText('<h2>circle<h2>')
def windowcomponents(self):
button=QPushButton("Add", self)
button.setGeometry(QRect(0, 0, 50, 28))
button.setIcon(QtGui.QIcon("addbutton.png"))
button.setToolTip("<h3>This is for creating random circles<h3>")
button.clicked.connect(self.paintcircle)
button=QPushButton("Generate Report", self)
button.setGeometry(QRect(49,0,150,28))
button.setIcon(QtGui.QIcon("generatereport.png"))
button.setToolTip("This is for generating pdf report of connection between two circles")
button=QPushButton("Save", self)
button.setGeometry(QRect(199,0,120,28))
button.setIcon(QtGui.QIcon("saveicon.png"))
button.setToolTip("This is for saving an image of canvas area")
def paintEvent(self, event):
super().paintEvent(event)
if self.should_paint_circle:
painter = QtGui.QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
painter.setPen(QPen(Qt.black, 5, Qt.SolidLine))
painter.drawEllipse(100, 100, 100, 100)
self.initUI()
self.label.move(60,100)
def paintcircle(self, painter):
self.should_paint_circle = True
self.update()
app = QApplication(sys.argv)
circle=Window()
circle.show()
sys.exit(app.exec_())
Widgets that are created with a parent, outside their __init__ (or their parent's), but not added to a layout, have to be explicitly shown; you're missing this:
self.label.show()
Besides that, you MUST NOT create new widgets within the paintEvent.
Painting is something that happens often, usually in the following situations (which happen very often:
when the widget is shown the first time
whenever the widget is hidden and shown again (for example, after minimizing and restoring the window)
whenever the mouse enters or exits it and/or its children
when the widget or any of its parents are resized
when a new children is shown
The result is that if you add a widget for each paint event, you'll probably end up with dozens (if not hundreds or thousands) of widgets, and, most importantly if you also show it, it will cause an infinite recursion.
class Window(QMainWindow):
def __init__(self):
super(Window,self).__init__()
title="layout management"
left=500
top=200
width=500
height=400
iconName="fosseeicon.jpg"
self.setWindowTitle(title)
self.setWindowIcon(QtGui.QIcon(iconName))
self.setGeometry(left, top, width, height)
self.should_paint_circle = False
self.windowcomponents()
self.label = QtWidgets.QLabel(self)
self.label.hide()
# ...
def paintEvent(self, event):
super().paintEvent(event)
if self.should_paint_circle:
painter = QtGui.QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
painter.setPen(QPen(Qt.black, 5, Qt.SolidLine))
painter.drawEllipse(100, 100, 100, 100)
def paintcircle(self, painter):
self.should_paint_circle = True
self.label.setText('<h2>circle<h2>')
self.label.move(60,100)
self.label.show()
self.update()
That said, based on this question and the previous one, I suggest you to study the documentation more carefully, especially what is related to the QMainWindow, the Layout management, painting in Qt and the related QPainter documentation.

How to fix missing Task Bar icon in second QMainWindow widget

I am making a GUI that had the Welcome page and the main page. The purpose is to let user agree on the welcome page, the welcome page is dismissed and the main page will show up for further step. However, the icon in the taskbar only shows up in the welcome page, when we click into the main window the icon is disappeared and the app appeared to be a minimized window on the bottom left corner in the screen.
The starting page and main window layout is appear like this.
class welcome_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(welcome_window, self).__init__(parent)
self.confirm_button = QtWidgets.QPushButton('Yes')
self.confirm_button.clicked.connect(self.startup)
Main_layout = QtWidgets.QHBoxLayout()
Main_layout.addWidget(self.confirm_button)
self.main.setLayout(Main_layout)
def startup(self):
self.close()
dialog = Main_window(self)
self.dialogs.append(dialog)
dialog.show()
class Main_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(Main_window, self).__init__(parent)
self.setGeometry(50, 50, 1500, 850)
# here is all the step for later operation
def main():
app = QtWidgets.QApplication(sys.argv)
main = welcome_window()
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I expected that if the icon located in the taskbar could always stay on, it would be great for my GUI. Thank you all.
First of all, the MRE you gave is not reproducible. When I tried to run it it just didn't work. In this case you had a simple issue so I could just guess what was intended, but when you get more complicated problems people might not be able to help you. So in the future please make sure that we can just copy-paste-execute your code.
The reason that the main window disappears is that it's a member of the Welcome window. When you close the Welcome window, the corresponding python object will deleted and therefore Python will no longer have a reference to the main window. The main window object will be garbage-collected and all kinds of strange things might happen (I would expect it to just disappear).
The solution is to have a reference to the main window that stays valid until the program closes. This can be done by defining it in the main function (and then giving it as a parameter to the Welcome window). Like this...
import sys
from PyQt5 import QtWidgets
# Use a QWidget if you don't need toolbars.
class welcome_window(QtWidgets.QWidget):
def __init__(self, main_window=None, parent = None):
super(welcome_window, self).__init__(parent)
self.main_window = main_window
self.confirm_button = QtWidgets.QPushButton('Yes')
self.confirm_button.clicked.connect(self.startup)
main_layout = QtWidgets.QHBoxLayout() # use lower case for variable names
main_layout.addWidget(self.confirm_button)
self.setLayout(main_layout)
def startup(self):
self.main_window.show()
self.close()
class Main_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(Main_window, self).__init__(parent)
self.setGeometry(50, 50, 1500, 850)
# here is all the step for later operation
# Don't use self.setLayout on a QMainWindow,
# use a central widget and set a layout on that.
self.main_widget = QtWidgets.QWidget()
self.setCentralWidget(self.main_widget)
main_layout = QtWidgets.QHBoxLayout()
self.main_widget.setLayout(main_layout)
main_layout.addWidget(QtWidgets.QLabel("Hello"))
def main():
app = QtWidgets.QApplication(sys.argv)
main = Main_window()
welcome = welcome_window(main_window=main)
welcome.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Some more tips. Don't use setLayout on a QMainWindow. Use a central widget and add your widgets to the layout of the central widget. The layout of the main window is for toolbars and such. See: https://doc.qt.io/qt-5/qmainwindow.html#qt-main-window-framework
Just use a QWidget if you want a simple window without toolbars (like your welcome window),
Best to use lower case for variable names and upper case for class names. E.g. I renamed Main_layout to main_layout. Look at the difference in syntax highlighting by Stack Overflow above.

Python 3 Tkinter button command not working (very specific scenario)

I am using these calendar modules found in this post for my program, with some slight modifications to the imports to make it work for the latest python version.
I'll just show the snippets of my code that I feel does matter to this problem.
So I have this pop up window that I made that I use for alerts:
#class for pop-up windows for alerts, errors etc.
class PopUpAlert():
def __init__(self, alert='Alert!'):
self.root = tk.Tk()
tk.Label(self.root,
text=alert,
font="Verdana 15",
fg='red',
padx=10,
pady=5).pack(side=tk.TOP)
self.root.bind('<Return>', (lambda event: self.ok()))
tk.Button(self.root,
text='ok',
pady=10,
command=self.ok).pack(side=tk.TOP)
def ok(self):
print('ok clicked')
self.root.destroy()
The function ok was made just for me to test if the function is even being called. This window works completely fine in my code, except when I try to implement with the calendar, where the "ok" button of my PopUpAlert (which is supposed to destroy the window) stops working:
class CalendarDialog(tkSimpleDialog.Dialog):
"""Dialog box that displays a calendar and returns the selected date"""
def body(self, master):
self.calendar = ttkcalendar.Calendar(master)
self.calendar.pack()
def apply(self):
self.result = self.calendar.selection
def validate(self):
if self.calendar.selection == None:
PopUpAlert(alert='Please select a date or click cancel!')
return False
return True
The calendar has an "ok" button that is used to confirm selection of the date and close the calendar window. What I was trying to do is make it such that the user cannot click "ok" to close the window if he/she has not picked a date. For that, I used the function validate which is pre-defined in the class tkSimpleDialog.Dialog which my CalendarDialog inherits from. I overwrote the function in my CalendarDialog class to call up PopUpAlert, then returned False to the parent function ok (which is called when the "Ok" button is pressed on the calendar window):
def ok(self, event=None):
if not self.validate():
self.initial_focus.focus_set() # put focus back
return
self.withdraw()
self.update_idletasks()
self.apply()
self.cancel()
def cancel(self, event=None):
# put focus back to the parent window
self.parent.focus_set()
self.destroy()
(The whole thing can be found in the tkSimpleDialog file that's linked in the other SO page that I linked above.)
After commenting out lines one by one I found that the "ok" button on my PopUpAlert only didn't work when self.root.destroy() isn't called on the calendar. Why? How do I fix this?
I already tried changing my PopUpAlert to be a Toplevel window, which also didn't work.
It would be a lot nicer of you to provide a mcve instead of asking us to make it.
The problem is that a dialog by default disables clicks to other windows, including windows it spawns. To fix this you need to use a Toplevel instead of Tk (as mentioned) AND add this line of code to the end of PopUpAlert.__init__:
self.root.grab_set()
It would be a lot neater if you subclassed Toplevel rather than that weird wrapper. Here's a mcve:
try:
import Tkinter as tk
import tkSimpleDialog as sd
except:
import tkinter as tk
from tkinter import simpledialog as sd
#class for pop-up windows for alerts, errors etc.
class PopUpAlert(tk.Toplevel):
def __init__(self, master, alert='Alert!', **kwargs):
tk.Toplevel.__init__(self, master, **kwargs)
tk.Label(self,
text=alert,
font="Verdana 15",
fg='red',
padx=10,
pady=5).pack(side=tk.TOP)
self.bind('<Return>', self.ok)
tk.Button(self,
text='ok',
pady=10,
command=self.ok).pack(side=tk.TOP)
self.grab_set() # this window only gets commands
def ok(self, *args):
print('ok clicked')
self.destroy()
class CalendarDialog(sd.Dialog):
"""Dialog box that displays a calendar and returns the selected date"""
def body(self, master):
self.calendar = tk.Label(master, text="Whatever you do, don't click 'OK'!")
self.calendar.pack()
def validate(self):
PopUpAlert(self, alert='Please select a date or click cancel!')
def display():
CalendarDialog(root)
root = tk.Tk()
tk.Button(root, text='data data data', command=display).pack()
root.mainloop()
Note I also got rid of that useless lambda, which happens to be a pet peeve of mine. lambda is great in some cases, but it's very rarely needed.

Overriding QLabel widget in PyQT

I am new to PyQt perhaps thats why facing this issue. I am trying to make a component inserter for excel sheets. For this purpose I am using QT for interface and using Qlabels within parent class of QMainWindow. On the basis of item selected from Qlist Widget, few Qlabels have to change on mainwindow dynamically Component inserter
As can be seen in above picture, the labels like WCAP-; Part Number and all below needs to change dynamically when the selected items change(when select button is clicked). But what happening is if I choose a different item from list, the previous Label stays and the new label is overlapping it as can be seen from picture below showing overlapping of labels
The code below shows that whenever button "Select" is pressed", label2 (Qlabel2) is formed, how can i delete the previous label whenever select button is pressed so that new Label dynamically replaces the old label.
Thanks a lot in advance.
def Display(self):
self.close()
label1 = QtGui.QLabel("Select the sheet",self)
label1.move(0,15)
self.listwidget = QtGui.QListWidget(self)
self.listwidget.move(0,40)
self.listwidget.resize(150,150)
for i in range(len(self.sheetnames)):
self.listwidget.addItem("%s"%self.sheetnames[i])
btn = QtGui.QPushButton('Select',self)
btn.resize(50,50)
btn.move(170,40)
btn.clicked.connect(self.Selected)
self.show()
def Selected(self):
self.close()
selecteditem = self.listwidget.currentItem().text()
self.sheetindex = self.sheetnames.index(selecteditem)
print self.sheetindex
aa = self.loadsheet.sheet_by_name(selecteditem)
global label2
label2 = QtGui.QLabel("",self)
label2.setText(selecteditem)
label2.move(0,190)
self.show()
self.InputParameters(aa)
You see a new QLabel because you create a new one every time you call Selected. I would initiate the UI at the creation of the widget (in the __init__ method):
def __init__(self):
self.label2 = QtGui.QLabel("",self)
And only update the text of the Qlabel when Selected is executed:
def Selected(self):
self.label2.setText(selecteditem)
About reinitializing all labels with an unknown number of labels and removing the old ones, you might want to look at QLabel.setParent(None). I wrote you a little example:
from PyQt4 import QtGui, QtCore
import sys
class test(QtGui.QWidget):
def __init__(self,parent=None):
self.widget=QtGui.QWidget.__init__(self, parent)
# Button to add labels
self.btnAdd = QtGui.QPushButton('Add')
self.btnAdd.connect(self.btnAdd, QtCore.SIGNAL('clicked()'),self.btnAddPressed)
# Button to remove labels
self.btnRemove = QtGui.QPushButton('Remove')
self.btnRemove.connect(self.btnRemove, QtCore.SIGNAL('clicked()'), self.btnRemovePressed)
# List to keep track of labels
self.labels=[]
# Layout
self.hbox = QtGui.QHBoxLayout()
self.hbox.addWidget(self.btnAdd)
self.hbox.addWidget(self.btnRemove)
self.setLayout(self.hbox)
self.show()
def btnAddPressed(self):
"""Adds a new label."""
self.labels.append(QtGui.QLabel("lbl"+str(len(self.labels)+1), self))
self.hbox.addWidget(self.labels[-1])
def btnRemovePressed(self):
"""Removes last label."""
self.labels[-1].setParent(None)
self.labels.pop(-1)
def main():
#Creating application
app = QtGui.QApplication(sys.argv)
main_win = test()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

How to see signals from QWidgets inside dynamically created QTabWidget pages?

EDIT : I've come up with a solution, and it's much more straightforward than I thought. Original code and question at the top. My solution after "The Question" below..
The Example
from PyQt4 import QtGui, QtCore
from example_Ui import Ui_MainWindow
from filler_Ui import Form
class TabFiller(Form):
def __init__(self, parent=None):
Form.__init__(self, parent)
def TabButtonClicked(self):
print("Tab button pressed.")
def LineEditChanged(self):
print("LineEdit contents edited in tab page!")
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
tab_filler = [] # create empty list for tab contents
tab_page = [] # create empty list for tab page
tab_count = 0
def CreateNewTab(self):
tab_title = "New Tab : " + str(self.tab_count)
self.tab_filler.append(TabFiller())
self.tab_filler[self.tab_count].label.setText(tab_title)
self.tab_page.append(self.tab_filler[self.tab_count])
self.tabWidget.addTab(self.tab_page[self.tab_count], tab_title)
self.tab_count += 1
def MainButtonPressed(self):
self.CreateNewTab()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
MainWindow contains a QTabWidget, which is a Button. clicked() signal has been defined in QtDesigner to be sent to the MainButtonPressed() function inside the MainWindow class.
Form widget also created in QTdesigner. Used to fill additional Tab Pages.
This contains a Button widget, and a LineEdit Widget.
The Question
I can't get my head around how I can tell which widget has been clicked or edited in each tab.
I know that each Tab Page is stored in the list called tab_page.
Within the MainWindow class, how would I receive a clicked() or finishedEditing() signal for a given widget in a currently active tab?
A Solution
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4 import QtGui
from example_Ui import Ui_MainWindow
from filler_Ui import Form
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
tab_index = 1 # 1 because we already made a default tab in QtDesigner
def LineEditChanged(self):
findWidget = self.tabWidget.widget(self.tabWidget.currentIndex()).findChildren(QtGui.QLineEdit, "lineEdit")
if findWidget[0].isModified() == True:
print("LineEdit contents edited in tab page!")
print("Name of page edited :", "'", self.tabWidget.tabText(self.tabWidget.currentIndex()),"'")
def TabButtonPressed(self):
print("YOU DID IT!")
print("Current Tab Index = ", self.tabWidget.currentIndex())
def CreateNewTab(self, tabNum):
tab_title = "New Tab : " + str(self.tab_index)
self.tabWidget.addTab(Form(), tab_title)
def MainButtonPressed(self):
self.CreateNewTab(self.tab_index)
findWidget = self.tabWidget.widget(self.tab_index).findChildren(QtGui.QPushButton, "tabButton")
findWidget[0].clicked.connect(self.TabButtonPressed)
findWidget = self.tabWidget.widget(self.tab_index).findChildren(QtGui.QLineEdit, "lineEdit")
findWidget[0].editingFinished.connect(self.LineEditChanged)
self.tab_index += 1
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Using this there's no need for storing each tab page object in a list. You basically use the QTabWidget to index your pages, and off you go.
If anyone has a more elegant way than this, please inform ;)
As outlined in my edited question, I did find the solution to this, which is to use the QTabWidget to "index" each dynamically created tab page.
In QtDesigner I created a main window with one QTabWidget and one button thusly;
Here's the object tree for that;
NOTE: I added a signal/slot for the "Click Me!" button in QtDesigner, so that when that button is clicked, the MainButtonPressed function is called.
To fill the tab pages, I also created a Form in QtDesigner, with a button and a QLineEdit widget;
And the object tree for that;
I'll reproduce the code here. NOTE: I've now updated this answer to use findChild rather than findChildren above:
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4 import QtGui
from example_Ui import Ui_MainWindow
from filler_Ui import Form
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
tab_index = 1 # 1 because we already made a default tab in QtDesigner
def LineEditChanged(self):
findWidget = self.tabWidget.widget(self.tabWidget.currentIndex()).findChild(QtGui.QLineEdit, "lineEdit")
if findWidget.isModified() == True:
print("LineEdit contents edited in tab page!")
print("Name of page edited :", "'", self.tabWidget.tabText(self.tabWidget.currentIndex()),"'")
def TabButtonPressed(self):
print("YOU DID IT!")
print("Current Tab Index = ", self.tabWidget.currentIndex())
def CreateNewTab(self, tabNum):
tab_title = "New Tab : " + str(self.tab_index)
self.tabWidget.addTab(Form(), tab_title)
def MainButtonPressed(self):
self.CreateNewTab(self.tab_index)
findWidget = self.tabWidget.widget(self.tab_index).findChild(QtGui.QPushButton, "tabButton")
findWidget.clicked.connect(self.TabButtonPressed)
findWidget = self.tabWidget.widget(self.tab_index).findChild(QtGui.QLineEdit, "lineEdit")
findWidget.editingFinished.connect(self.LineEditChanged)
self.tab_index += 1
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
When run, pressing the "Click Me!" button on the main tab page creates a new tab, and adds the contents of the "filler" page to it.
The variable tab_index keeps track of how many tabs there are and allows you to reference the contents of each tab.
To find a widget in a tab, you use the findChild function of Qt;
findWidget = self.tabWidget.widget(self.tab_index).findChild(QtGui.QPushButton, "tabButton")
Finding a specific widget is straightforward. You specify the type of widget you're looking for (QtGui.QPushButton) , and the name you assigned it in QtDesigner (tabButton)
In this case the found widget can be referenced by the variable findWidget.
You can then connect signals to function slots as usual;
findWidget.clicked.connect(self.TabButtonPressed)
In this case I used the new-style signal connection method to connect the clicked() signal to a function named TabButtonPressed in my program.
Rinse and repeat for each widget on the Tab Page you wish to do something with.
After that, it really is plain sailing ;)
I hope this information helps others in their GUI endeavours. You can probably use the same technique with the QToolBox widget.

Resources