editable QComboBox InsertPolicy=QComboBox.AtTop doesn't insert new text - python-3.x

i tried to implement a editable QComboBox. But if you write a new item in the QLineEdit of the editable QComboBox this element does not appear in the list of QComboBox.
Must i insert the new text additionally by code?
Here is my code:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import \
QWidget, QComboBox, QApplication, QVBoxLayout, QPushButton
class MyEditableComboBox(QComboBox):
def __init__(self, parent=None, insert_at=QComboBox.InsertAtTop):
super().__init__(parent)
self.setEditable(True)
# we wanna some new data
self.setInsertPolicy(insert_at)
# the new text should be inserted
class Window(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
lyt = QVBoxLayout(self)
self.setLayout(lyt)
items = ["Burger",
"Chicken Wings",
"Pizza",
"Toast"]
self.cbo = MyEditableComboBox(self)
self.cbo.setMinimumWidth(200)
self.cbo.addItems(items)
lyt.addWidget(self.cbo)
self.btn = QPushButton(self)
self.btn.setText("Show Content of cbo")
self.btn.clicked.connect(self._show_content_of_cbo)
lyt.addWidget(self.btn)
def _show_content_of_cbo(self):
print("=" * 70)
cbo = self.cbo
for i in range(cbo.count()):
print("{}: {}".format(i, cbo.itemText(i)))
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
If you insert a "Steak" in the QLineEdit of the editable and then clicking on the button i saw the output:
======================================================================
0: Burger
1: Chicken Wings
2: Pizza
3: Toast

Short answer: After typing "Steak" press the Enter or Return key.
Documentation seems unclear when an item is added or not. It is not enough that you write for it to be added, but the QComboBox uses the returnPressed signal of the QLineEdit to add the item. So the solution is to execute some action that emits that signal like pressing the Enter or Return key.

Related

How to create contextmenu to delete row for inside of qtableview python

below is the code i am using:
How to create contextmenu to delete row for inside of qtableview python.
It is showing menu even i clicked on Qpushbutton also but i need to show the delete menu on only inside of qtableview. and let me know the way to delete the row of qtableview data.
Please let me know the solution.
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5 import uic
import sys
import sqlite3
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
uic.loadUi("tableview.ui", self)
self.show()
self.view_msa()
self.pushButton.clicked.connect(self.view_msa)
def view_msa(self):
self.model = QtGui.QStandardItemModel(self)
self.tableView.setModel(self.model)
conn = sqlite3.connect('book.db')
cur = conn.cursor()
cur.execute("SELECT * FROM card")
db_data = cur.fetchall()
print(db_data)
for line in db_data:
row = []
for item in line:
cell = QStandardItem(str(item))
row.append(cell)
self.model.setHorizontalHeaderLabels(["Name","Age","Gender"])
self.model.appendRow(row)
def contextMenuEvent(self, event):
self.click_menu = QtWidgets.QMenu()
renameAction = QtWidgets.QAction('Delete', self)
renameAction.triggered.connect(lambda: self.renameSlot(event))
self.click_menu.addAction(renameAction)
self.click_menu.popup(QtGui.QCursor.pos())
def renameSlot(self, event):
print("Renameing slot called")
app = QApplication(sys.argv)
window = UI()
app.exec_()
All widget have the contextMenuPolicy property, which if set to QtCore.Qt.CustomContextMenu allows to connect to the customContextMenuRequested(pos) signal (note that the pos is in widget coordinates). From there you can access the index that is at the mouse position through indexAt or, better, get the selectedIndexes (which is useful if you have multiple selection enabled).
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
# ...
self.tableView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tableView.customContextMenuRequested.connect(self.tableMenu)
def tableMenu(self, pos):
selected = self.tableView.selectedIndexes()
if not selected:
return
menu = QtWidgets.QMenu()
deleteAction = menu.addAction('Delete rows')
deleteAction.triggered.connect(lambda: self.removeRows(selected))
menu.exec_(QtGui.QCursor.pos())
def removeRows(self, indexes):
# get unique row numbers
rows = set(index.row() for index in indexes)
# remove rows in *REVERSE* order!
for row in sorted(rows, reverse=True):
self.model.removeRow(row)
Note that in this case you have to use menu.exec() (which is usually the preferred method), not popup(), otherwise the function will return instantly and the menu won't probably shown up because of internal processing of the mouse events: exec blocks until it returns (whether any of its actions are triggered or it's closed), popup returns immediately.

How to set The push button should only work when two line edit input widgets contain text

I have created two line Edit input widgets and one push button.
I need to set The push button should only work when two line edits contain text.
I need to set two line edit input widgets as mandatory.
You can connect the QLineEdit.textEdited signal to a function to check if all the required fields contain text, and enable/disable the button accordingly. Here is an example:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Template(QWidget):
def __init__(self):
super().__init__()
self.f1 = QLineEdit()
self.f1.textEdited.connect(self.check_input)
self.f2 = QLineEdit()
self.f2.textEdited.connect(self.check_input)
self.btn = QPushButton('Enter')
self.btn.setDisabled(True)
form = QFormLayout(self)
form.addRow('Field 1', self.f1)
form.addRow('Field 2', self.f2)
form.addRow(self.btn)
def check_input(self):
if self.f1.text() and self.f2.text():
self.btn.setEnabled(True)
else:
self.btn.setDisabled(True)
if __name__ == '__main__':
app = QApplication(sys.argv)
gui = Template()
gui.show()
sys.exit(app.exec_())

How to connect in realtime two QCombobox

I working on the PyQt5 ui framework. How to connect in realtime two QCombobox so that the QCombobox 2 shall load a data based on the text in QCombobox1.
Here is a very small example. The items in the second combo box are changed by connecting a slot to the textChanged signal of the first combobox. I use a dictionary to look up which items should be displayed in the second combo box depending on the current text in the first combobox.
from PyQt5.QtWidgets import QWidget, QApplication, QComboBox, QFormLayout
class Widget(QWidget):
def __init__(self, parent = None):
super().__init__(parent)
self.categories = {'animals':['cat', 'dog', 'parrot', 'fish'],
'flowers':['daisies', 'tulips', 'daffodils', 'roses'],
'colors':['red', 'orange', 'blue', 'purple']}
self.cat_combobox = QComboBox(self)
self.item_combobox = QComboBox(self)
self.cat_combobox.setEditable(False)
self.item_combobox.setEditable(False)
self.cat_combobox.currentTextChanged.connect(self.set_category)
self.cat_combobox.addItems(sorted(self.categories.keys()))
form_layout = QFormLayout(self)
form_layout.addRow('Category', self.cat_combobox)
form_layout.addRow('Items', self.item_combobox)
def set_category(self, text):
self.item_combobox.clear()
self.item_combobox.addItems(self.categories.get(text, []))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Widget()
window.show()
app.exec()

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.

QMainWindow flashes and disappears when called from another QMainWindow

This fairly minimal code creates a systray item with three right click options. One is an instance of QDialog, another QMainWindow, and also Quit. It's for a systray-driven app that will have some qdialogs and also a qmainwindow containing a table widget (or, is it possible to create a table into a qdialog?). I've been stuck on this for a week or so, read a lot of related materials and do not understand how to resolve it.
From the systray icon menu, clicking on QDialog, the dialog opens, waits for user action, and clicking the Ok or Cancel buttons will print which one was clicked. It would seem this works because QDialog has its own exec_(). Great so far.
However, clicking on the QMainWindow menu option, the main window dialog appears briefly and disappears with no chance for user input, of course. Maybe instead, the qmainwindow dialog would need to rely on the QApplication's exec_() where the object would instead be initialized just before app.exec_() perhaps? If that would work, I'm not clear on how def qmainwindow() would retrieve the information back from the user.
Hopefully a simple matter for someone who knows, a few changes, bingo.
Current environment: Windows 7 or XP, Python 2.7, Pyside
If you run this, there will be a blank place-holder in the systray that is clickable (right click), or you can also give it an actual image in place of 'sample.png'.
#!python
from PySide import QtGui, QtCore
from PySide.QtGui import QApplication, QDialog, QMainWindow
def qdialog():
qdialog_class_obj = TestClassQDialog()
qdialog_class_obj.show()
qdialog_class_obj.exec_() # wait for user
print "qdialog_user_action: ", qdialog_class_obj.qdialog_user_action
def qmainwindow():
qmainwindow_class_obj = TestClassQMainWindow()
qmainwindow_class_obj.show()
#qmainwindow_class_obj.exec_() # 'TestClassQMainWindow' object has no attribute 'exec_'
class TestClassQDialog(QDialog):
def __init__(self, parent=None):
super(TestClassQDialog, self).__init__(parent)
self.ok_cancel = QtGui.QDialogButtonBox(self)
self.ok_cancel.setStandardButtons(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel)
QtCore.QObject.connect(self.ok_cancel, QtCore.SIGNAL("accepted()"), self.button_ok)
QtCore.QObject.connect(self.ok_cancel, QtCore.SIGNAL("rejected()"), self.button_cancel)
def button_ok(self):
self.qdialog_user_action = 'ok'
self.hide()
def button_cancel(self):
self.qdialog_user_action = 'cancel'
self.hide()
class TestClassQMainWindow(QMainWindow):
def __init__(self, parent=None):
super(TestClassQMainWindow, self).__init__(parent)
self.ok_cancel = QtGui.QDialogButtonBox(self)
self.ok_cancel.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
QtCore.QObject.connect(self.ok_cancel, QtCore.SIGNAL("accepted()"), self.button_ok)
QtCore.QObject.connect(self.ok_cancel, QtCore.SIGNAL("rejected()"), self.button_cancel)
def button_ok(self):
self.hide()
def button_cancel(self):
self.hide()
class SysTrayIcon(QMainWindow):
def __init__(self, parent=None):
super(SysTrayIcon, self).__init__(parent)
self.qdialog_action = QtGui.QAction("QDialog", self, triggered=qdialog)
self.qmainwindow_action = QtGui.QAction("QMainWindow", self, triggered=qmainwindow)
self.quit_action = QtGui.QAction("Quit", self, triggered=QtGui.qApp.quit)
self.createSystrayIcon()
self.systrayIcon.show()
def createSystrayIcon(self):
self.systrayIconMenu = QtGui.QMenu(self)
self.systrayIconMenu.addAction(self.qdialog_action)
self.systrayIconMenu.addAction(self.qmainwindow_action)
self.systrayIconMenu.addSeparator()
self.systrayIconMenu.addAction(self.quit_action)
self.systrayIcon = QtGui.QSystemTrayIcon(self)
self.systrayIcon.setContextMenu(self.systrayIconMenu)
self.systrayIcon.setIcon(QtGui.QIcon('sample.png')) # point to a valid image if you want.
self.systrayIcon.setVisible(True)
if __name__ == '__main__':
app = QtGui.QApplication([])
systrayicon = SysTrayIcon()
app.exec_()
I've got it working. What I did was move your external method qmainwindow inside of your
SysTrayIcon and created the class parameter self.qmainwindow_class_obj = TestClassQMainWindow(). I've attached the working code below. Also, you're using the old style signal slot method, I take it you're coming from old school PyQt. The new method if very nice, clean and pythonic. I've also put the new style methods in the below code. Another thing I would do is move your qdialog method inside the SysTrayIcon class. I don't really understand why you have it outside the class but maybe I'm missing something. Hope this helps.
#!python
from PySide import QtGui, QtCore
from PySide.QtGui import QApplication, QDialog, QMainWindow
def qdialog():
qdialog_class_obj = TestClassQDialog()
qdialog_class_obj.show()
qdialog_class_obj.exec_() # wait for user
print "qdialog_user_action: ", qdialog_class_obj.qdialog_user_action
class TestClassQDialog(QDialog):
def __init__(self, parent=None):
super(TestClassQDialog, self).__init__(parent)
self.ok_cancel = QtGui.QDialogButtonBox(self)
self.ok_cancel.setStandardButtons(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel)
self.ok_cancel.accepted.connect(self.button_ok)
self.ok_cancel.rejected.connect(self.button_cancel)
def button_ok(self):
self.qdialog_user_action = 'ok'
self.hide()
def button_cancel(self):
self.qdialog_user_action = 'cancel'
self.hide()
class TestClassQMainWindow(QMainWindow):
def __init__(self, parent=None):
super(TestClassQMainWindow, self).__init__(parent)
self.ok_cancel = QtGui.QDialogButtonBox(self)
self.ok_cancel.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.ok_cancel.accepted.connect(self.button_ok)
self.ok_cancel.rejected.connect(self.button_cancel)
def button_ok(self):
self.hide()
def button_cancel(self):
self.hide()
class SysTrayIcon(QMainWindow):
def __init__(self, parent=None):
super(SysTrayIcon, self).__init__(parent)
self.qmainwindow_class_obj = TestClassQMainWindow()
self.qdialog_action = QtGui.QAction("QDialog", self, triggered=qdialog)
self.qmainwindow_action = QtGui.QAction("QMainWindow", self, triggered=self.qmainwindow)
self.quit_action = QtGui.QAction("Quit", self, triggered=QtGui.qApp.quit)
self.createSystrayIcon()
self.systrayIcon.show()
def createSystrayIcon(self):
self.systrayIconMenu = QtGui.QMenu(self)
self.systrayIconMenu.addAction(self.qdialog_action)
self.systrayIconMenu.addAction(self.qmainwindow_action)
self.systrayIconMenu.addSeparator()
self.systrayIconMenu.addAction(self.quit_action)
self.systrayIcon = QtGui.QSystemTrayIcon(self)
self.systrayIcon.setContextMenu(self.systrayIconMenu)
self.systrayIcon.setIcon(QtGui.QIcon('linux.jpeg')) # point to a valid image if you want.
self.systrayIcon.setVisible(True)
def qmainwindow(self):
self.qmainwindow_class_obj.show()
if __name__ == '__main__':
app = QtGui.QApplication([])
systrayicon = SysTrayIcon()
app.exec_()

Resources