PyQt UI shows in new window but widgets don't activate signals - user-interface

I've looked through every thread here I could find, but I haven't seen a question like the one I have.
I have a GUI I created in Qt Designer called app_ui.py (after I pyuic5 it). This is the main application window. I created a new window so the user can adjust some settings. This window is called settings_ui.py. Here's the applicable code in the main application for both:
import os
import sys
import glob
from app_ui import Ui_MainWindow
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import pyqtSlot
from settings_ui import Ui_Settings
class SettingsWindow(QtWidgets.QMainWindow):
def __init__(self):
# Bring in the ui elements
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_Settings()
self.ui.setupUi(self)
.
. (application logic here)
.
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
# Bring in the ui elements
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.setWindowTitle(f"{self.windowTitle()} ** Version: {__version__} **")
self.ui.actionSettings.triggered.connect(self.showSettings)
.
. (application logic here)
.
def showSettings(self):
self.winset = QtWidgets.QMainWindow()
self.displaySettings = Ui_Settings()
self.displaySettings.setupUi(self.winset)
self.winset.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = ApplicationWindow()
window.show()
sys.exit(app.exec())
Note: I did use super(Window_Name, self).__init__() to initialize my windows in the past, but read somewhere that it's more Pythonic to initialize as QtWidgets.QMainWindow.__init__(self). I don't know if that's true of not, but both ways work.
When I execute the main application and click on the Settings button, the settings window displays as it should, but none of the application logic works (e.g.: You can press buttons, but the connect events don't fire. The style-sheets don't get applied, etc.).
If I pull out the SettingsWindow class into it's own file with the same if __name__ == "__main__" it works as intended.
What am I doing wrong that prevents the Settings Window from working through the main application?
EDIT: Adding minimal reproducible code and making title more descriptive.
Here's the non-working application:
# -*- coding: utf-8 -*-
import os
import sys
import glob
from app_ui import Ui_MainWindow
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import pyqtSlot
from settings_ui import Ui_Settings
class SettingsWindow(QtWidgets.QMainWindow):
def __init__(self):
# Bring in the ui elements
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_Settings()
self.ui.setupUi(self)
self.ui.btnChange.clicked.connect(self.changeColor)
self.colorArray = ['black','blue','red','green']
self.currIndex = 0
def changeColor(self):
style = f"color: {self.colorArray[self.currIndex]}"
if self.currIndex < 2:
self.currIndex += 1
else:
self.currIndex = 0
self.ui.lblChange.setStyleSheet(style)
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
# Bring in the ui elements
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.btnSettings.clicked.connect(self.showSettings)
def showSettings(self):
self.winset = QtWidgets.QMainWindow()
self.displaySettings = Ui_Settings()
self.displaySettings.setupUi(self.winset)
self.winset.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = ApplicationWindow()
window.show()
sys.exit(app.exec())
Here's the working standalone SettingsWindow:
# -*- coding: utf-8 -*-
import os
import sys
import glob
from app_ui import Ui_MainWindow
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import pyqtSlot
from settings_ui import Ui_Settings
class SettingsWindow(QtWidgets.QMainWindow):
def __init__(self):
# Bring in the ui elements
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_Settings()
self.ui.setupUi(self)
self.ui.btnChange.clicked.connect(self.changeColor)
self.colorArray = ['black','blue','red','green']
self.currIndex = 0
def changeColor(self):
style = f"color: {self.colorArray[self.currIndex]}"
if self.currIndex < 2:
self.currIndex += 1
else:
self.currIndex = 0
self.ui.lblChange.setStyleSheet(style)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = SettingsWindow()
window.show()
sys.exit(app.exec())
Here's the ui.py files
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'settings.ui'
#
# Created by: PyQt5 UI code generator 5.12.3
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Settings(object):
def setupUi(self, Settings):
Settings.setObjectName("Settings")
Settings.resize(307, 252)
self.centralwidget = QtWidgets.QWidget(Settings)
self.centralwidget.setObjectName("centralwidget")
self.lblChange = QtWidgets.QLabel(self.centralwidget)
self.lblChange.setGeometry(QtCore.QRect(30, 30, 251, 61))
font = QtGui.QFont()
font.setPointSize(16)
self.lblChange.setFont(font)
self.lblChange.setObjectName("lblChange")
self.btnChange = QtWidgets.QPushButton(self.centralwidget)
self.btnChange.setGeometry(QtCore.QRect(100, 120, 121, 24))
self.btnChange.setObjectName("btnChange")
Settings.setCentralWidget(self.centralwidget)
self.retranslateUi(Settings)
QtCore.QMetaObject.connectSlotsByName(Settings)
def retranslateUi(self, Settings):
_translate = QtCore.QCoreApplication.translate
Settings.setWindowTitle(_translate("Settings", "MainWindow"))
self.lblChange.setText(_translate("Settings", "This should change colors"))
self.btnChange.setText(_translate("Settings", "Change Color"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Settings = QtWidgets.QMainWindow()
ui = Ui_Settings()
ui.setupUi(Settings)
Settings.show()
sys.exit(app.exec_())
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'app.ui'
#
# Created by: PyQt5 UI code generator 5.12.3
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
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.btnSettings = QtWidgets.QPushButton(self.centralwidget)
self.btnSettings.setGeometry(QtCore.QRect(70, 40, 75, 24))
self.btnSettings.setObjectName("btnSettings")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.btnSettings.setText(_translate("MainWindow", "Settings"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Although it's not actually changing any settings, the above code shows the issue I'm having.

Try it:
#import os
import sys
#import glob
from PyQt5 import QtCore, QtWidgets, QtGui
#from PyQt5.QtCore import pyqtSlot
#from settings_ui import Ui_Settings
class Ui_Settings(object):
def setupUi(self, Settings):
Settings.setObjectName("Settings")
Settings.resize(307, 252)
self.centralwidget = QtWidgets.QWidget(Settings)
self.centralwidget.setObjectName("centralwidget")
self.lblChange = QtWidgets.QLabel(self.centralwidget)
self.lblChange.setGeometry(QtCore.QRect(30, 30, 251, 61))
font = QtGui.QFont()
font.setPointSize(16)
self.lblChange.setFont(font)
self.lblChange.setObjectName("lblChange")
self.btnChange = QtWidgets.QPushButton(self.centralwidget)
self.btnChange.setGeometry(QtCore.QRect(100, 120, 121, 24))
self.btnChange.setObjectName("btnChange")
Settings.setCentralWidget(self.centralwidget)
self.retranslateUi(Settings)
QtCore.QMetaObject.connectSlotsByName(Settings)
def retranslateUi(self, Settings):
_translate = QtCore.QCoreApplication.translate
Settings.setWindowTitle(_translate("Settings", "MainWindow"))
self.lblChange.setText(_translate("Settings", "This should change colors"))
self.btnChange.setText(_translate("Settings", "Change Color"))
#from app_ui import Ui_MainWindow
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.btnSettings = QtWidgets.QPushButton(self.centralwidget)
self.btnSettings.setGeometry(QtCore.QRect(70, 40, 75, 24))
self.btnSettings.setObjectName("btnSettings")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.btnSettings.setText(_translate("MainWindow", "Settings"))
class SettingsWindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_Settings()
self.ui.setupUi(self)
self.ui.btnChange.clicked.connect(self.changeColor)
self.colorArray = ['black', 'blue', 'red', 'green']
self.currIndex = 0
def changeColor(self):
style = f"color: {self.colorArray[self.currIndex]}"
if self.currIndex < 2:
self.currIndex += 1
else:
self.currIndex = 0
self.ui.lblChange.setStyleSheet(style)
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.btnSettings.clicked.connect(self.showSettings)
def showSettings(self):
# self.winset = QtWidgets.QMainWindow() # ---
# self.displaySettings = Ui_Settings() # ---
# self.displaySettings.setupUi(self.winset) # ---
self.winset = SettingsWindow() # +++
self.winset.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = ApplicationWindow()
window.show()
sys.exit(app.exec())
Update
This works, but it doesn't even try to answer my question. What am I doing wrong that prevents the Settings Window from working through the main application?
The UI elements have to stay in their own file, I can't combine everything into a single file.
Nothing prevents the settings window from working through the main application.
You have written the logic in the SettingsWindow cadass, but nowhere do you instantiate this class to use this logic.
I noted in the code what needs to be removed and what needs to be added. You do not need to create an instance of self.displaySettings, since you have already created self.ui in the SettingsWindow class. You just need to create self.winset = SettingsWindow () and that's it.
You can combine everything into one file or use separate files - this does not change the essence.
main.py
#import os
import sys
#import glob
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import pyqtSlot
from settings_ui import Ui_Settings
from app_ui import Ui_MainWindow
class SettingsWindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_Settings()
self.ui.setupUi(self)
self.ui.btnChange.clicked.connect(self.changeColor)
self.colorArray = ['black', 'blue', 'red', 'green']
self.currIndex = 0
def changeColor(self):
style = f"color: {self.colorArray[self.currIndex]}"
if self.currIndex < 2:
self.currIndex += 1
else:
self.currIndex = 0
self.ui.lblChange.setStyleSheet(style)
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.btnSettings.clicked.connect(self.showSettings)
def showSettings(self):
# self.winset = QtWidgets.QMainWindow() # ---
# self.displaySettings = Ui_Settings() # ---
# self.displaySettings.setupUi(self.winset) # ---
self.winset = SettingsWindow() # +++
self.winset.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = ApplicationWindow()
window.show()
sys.exit(app.exec())

Related

Change QPushButton behaviour if hotkey is held ?

Is it possible to change the appearance and function of a QPushButton , such as hover , if a hotkey is held ?
I’m after a solution whereby if I held CTRL down , then hovered , then pressed, it would have a different outcome to hover and press
I’m using Pyside currently and this application is for maya
Here's an example subclassing QPushButton's enter and leave events. It will change stylesheets when ctrl is held down, and will also execute different function than when ctrl is not pressed:
from PySide2 import QtCore, QtGui, QtWidgets
class CustomButton(QtWidgets.QPushButton):
def __init__(self, label, parent=None):
super(CustomButton, self).__init__(label, parent)
self.entered = False # Track when the cursor enters this widget.
self.normal_style = "QPushButton {background-color:red;}"
self.alt_style = "QPushButton {background-color:blue;}"
self.setStyleSheet(self.normal_style)
self.clicked.connect(self.click_event)
def enterEvent(self, event):
self.entered = True
self.set_style()
def leaveEvent(self, event):
self.entered = False
self.setStyleSheet(self.normal_style)
def set_style(self):
if self.entered and self.parent().is_ctrl_down:
self.setStyleSheet(self.alt_style)
else:
self.setStyleSheet(self.normal_style)
def func_1(self):
print "1"
def func_2(self):
print "2"
def click_event(self):
if self.entered and self.parent().is_ctrl_down:
self.func_2()
else:
self.func_1()
class Window(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.is_ctrl_down = False # Track when ctrl is held down.
self.my_button = CustomButton("Hello World!", parent=self)
self.main_layout = QtWidgets.QVBoxLayout()
self.main_layout.addWidget(self.my_button)
self.setLayout(self.main_layout)
self.resize(400, 400)
self.setWindowTitle("Button behaviour example")
def keyPressEvent(self, event):
ctrl_state = event.modifiers() == QtCore.Qt.CTRL
if ctrl_state != self.is_ctrl_down:
self.is_ctrl_down = ctrl_state
self.my_button.set_style()
def keyReleaseEvent(self, event):
self.is_ctrl_down = False
self.my_button.set_style()
tool = Window()
tool.show()
I tested this on Maya 2018, so it's in PySide2. If you're using an older version with PySide you just need minor tweaks to make this example work.

How to update the Wxpython statusbar with run-time data streaming in another *.py file

my project has many python files,and right now the problem is when I clicked button in Main-UI interface,it will invoke some function in another python file(sub-program),and i need all the running status in sub-program will also be updated in main-UI,how do i accomplish this?
The thing i can try so far i known is using socket,but i want to know do you guys have any other good ideas on this?
The code like this:
1. Main-UI:
import wx,time
from threading import Thread
from path import basicTest
EVT_RESULT_ID = wx.NewId()
def EVT_RESULT(win, func):
win.Connect(-1, -1, EVT_RESULT_ID, func)
class ResultEvent(wx.PyEvent):
def __init__(self, data):
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data
class TestThread(Thread):
def __init__(self, wxObject):
Thread.__init__(self)
self.wxObject = wxObject
self.start()
def run(self):
this masked sub-script can run,but what i want to do is to replace it with invoking from another python file
'''
for i in range(6):
time.sleep(1)
wx.PostEvent(self.wxObject, ResultEvent(i))
'''
data = basicTest().run(10)
wx.PostEvent(self.wxObject, ResultEvent(data))
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Tester")
panel = wx.Panel(self, wx.ID_ANY)
self.btn = wx.Button(panel, label="Start Test")
self.statusbar = self.CreateStatusBar()
self.btn.Bind(wx.EVT_BUTTON, self.onButton)
EVT_RESULT(self, self.updateStatus)
def onButton(self, event):
TestThread(self)
btn = event.GetEventObject()
btn.Disable()
def updateStatus(self, msg):
t = msg.data
self.statusbar.SetStatusText("Sequence %i running.." %t)
self.btn.Enable()
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyForm().Show()
app.MainLoop()
sub-script:
import time
class basicTest():
def run(self,inter):
for i in range(inter):
return i
time.sleep(1)
As list above and also i known that the main UI only updated when the sub-script finished,it's not run-time refresh from another sub,can anyone help me, very appreciate
I would just leave the long running code in its thread. Then you can have wxPython start the thread. In your thread's run method, just use one of wxPython's thread-safe methods to call your UI. I would recommend wx.CallAfter or 'wx.PostEvent'.
Once you have that done, then you just execute the necessary method in your main UI.

How to build a wx.TreeControl with a list of file paths?

I want to create a TreeControl which will be a side panel containing filenames of images. How can I generate a tree with a list of file paths for the wx.treeControl?
Example of file paths
C:\Program Files\Windows Sidebar\Gadgets\Weather.Gadget\images\120DPI\(120DPI)alertIcon.png
C:\Program Files\Windows Sidebar\Gadgets\Weather.Gadget\images\120DPI\(120DPI)grayStateIcon.png
C:\Program Files\Windows Sidebar\Gadgets\Weather.Gadget\images\120DPI\(120DPI)greenStateIcon.png
C:\Program Files\Windows Sidebar\Gadgets\Weather.Gadget\images\120DPI\(120DPI)notConnectedStateIcon.png
C:\Program Files\Windows Sidebar\Gadgets\Weather.Gadget\images\144DPI\(144DPI)alertIcon.png
I want to be able to put these into a directory tree using the wx.TreeControl
Couldn't you just use the GenericDirCtrl or maybe the MultiDirDialog? If you really want to do your own thing, then this silly example should get you started:
import glob
import os
import wx
########################################################################
class MyTreeCtrl(wx.TreeCtrl):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.TreeCtrl.__init__(self, parent)
########################################################################
class MyPanel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
path = r'C:\Users\mdriscoll\Documents'
paths = glob.glob(path + "/*")
self.tree = MyTreeCtrl(self)
isz = (16,16)
il = wx.ImageList(isz[0], isz[1])
fldridx = il.Add(wx.ArtProvider_GetBitmap(wx.ART_FOLDER,
wx.ART_OTHER, isz))
fldropenidx = il.Add(wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN,
wx.ART_OTHER, isz))
self.tree.SetImageList(il)
self.root = self.tree.AddRoot(os.path.basename(path))
self.tree.SetPyData(self.root, None)
self.tree.SetItemImage(self.root, fldridx, wx.TreeItemIcon_Normal)
self.tree.SetItemImage(self.root, fldropenidx, wx.TreeItemIcon_Expanded)
for item in paths:
if os.path.isdir(item):
child = self.tree.AppendItem(self.root, os.path.basename(item))
self.tree.SetPyData(child, None)
self.tree.SetItemImage(child, fldridx, wx.TreeItemIcon_Normal)
self.tree.SetItemImage(child, fldropenidx, wx.TreeItemIcon_Expanded)
self.tree.Expand(self.root)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.tree, 1, wx.EXPAND)
self.SetSizer(sizer)
########################################################################
class MyFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
super(MyFrame, self).__init__(None, title="TreeCtrl Example")
panel = MyPanel(self)
self.Show()
if __name__ == "__main__":
app = wx.App()
frame = MyFrame()
app.MainLoop()

Why wxframe isn't raised from a function called with global gtk binder?

Ok, why this simple app dosn't work.
I've spent one day investigating this and got nothing.
import wx, os
import gtk
import keybinder
class FrameWithHotKey(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
keybinder.bind("<Ctrl>period", self.toggle_shown)
def toggle_shown(self):
# windowNow id
if self.IsShown():
self.Hide()
else:
self.Show()
self.Raise()
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = FrameWithHotKey(None)
app.MainLoop()
I don't know why, but sometimes (especially when I raise apps by clicking on them on panel) raising dosen't work and I got flash icon instead of raised window.
UPDATE
Ok, i return to the topic and notice these..
above example works for me.. strange
i isolated strange behaviour which below code shows.. it's something related with wnck lib. So if my app window is deactivated by open new window (left click on window - test1) then raise works perfect, but if other window (replace 'opera' with any of Yours) is activated with wnck(by right click - test2) then actvation fails
import logging
import subprocess
import time
import wnck
import wx
logging.basicConfig(level=logging.DEBUG)
class MyFrame(wx.Frame):
def __init__(self, parent, title=''):
wx.Frame.__init__(self, parent, title=title)
self.Centre()
self.Bind(wx.EVT_LEFT_DOWN, self.test1)
self.Bind(wx.EVT_RIGHT_DOWN, self.raise_window)
def test1(self, evt):
logging.debug('losing..')
subprocess.Popen(['xterm'])
time.sleep(1)
self.Raise()
logging.debug('lost')
def lose_focus_by_wnck(self):
screen = wnck.screen_get_default()
import gtk
while gtk.events_pending():
gtk.main_iteration(False)
wins = screen.get_windows()
logging.debug('wins: {0}'.format(wins))
for win in wins:
app_name = win.get_application().get_name()
logging.debug('app: {0}'.format(app_name))
if 'opera' in app_name.lower():
win_id = win.get_xid()
break
else:
win_id = None
return win_id
def test2(self, evt):
logging.debug('losing..')
win_id = self.lose_focus_by_wnck()
win = wnck.window_get(win_id)
TIMESTAMP = 0
win.activate(TIMESTAMP)
logging.debug('lost')
time.sleep(1)
self.Raise()
logging.debug('raised')
if name == 'main':
app = wx.PySimpleApp(redirect=False)
frame = MyFrame(None)
frame.Show()
app.MainLoop()
Does anybody understand this behaviour instead of very helpful wtf like i feel? :)
What is keybinder? Are you using an AcceleratorTable? See http://www.blog.pythonlibrary.org/2010/12/02/wxpython-keyboard-shortcuts-accelerators/ for more info. I don't think you can mix pyGtk with wxPython.

model/view QCompleter in a QLineEdit

ubuntu 10.04, KDE 4.4.5
python 2.6.4
qt 4.6.2
pyqt 4.6.2
I'm trying to create a QCompleter, which works fine if I just build the QLineEdit.
However if I drop the QLineEdit into a QMainWindow, the QCompleter no longer works.
Here is the LineEdit class
# LineEdit class
import sys
from PyQt4 import QtCore, QtGui
class LineEdit(QtGui.QLineEdit):
def __init__(self, parent=None):
super(LineEdit, self).__init__(parent)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.completer = QtGui.QCompleter(self)
self.completer.setCompletionMode(QtGui.QCompleter.UnfilteredPopupCompletion)
self.pFilterModel = QtGui.QSortFilterProxyModel(self)
self.pFilterModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.completer.setPopup(self.view())
self.setCompleter(self.completer)
self.textEdited[unicode].connect(self.pFilterModel.setFilterFixedString)
def setModel(self, model):
self.pFilterModel.setSourceModel(model)
self.completer.setModel(self.pFilterModel)
def setModelColumn( self, column ):
self.completer.setCompletionColumn(column)
self.pFilterModel.setFilterKeyColumn(column)
def view(self):
return self.completer.popup()
def index( self ):
return self.currentIndex()
The QCompleter works if I build LinEdit this way
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
model = QtGui.QStandardItemModel()
for i,word in enumerate(['test', 'blah', 'heh', 'yep']):
item = QtGui.QStandardItem(word)
model.setItem(i, 0, item)
lineEdit = LineEdit()
lineEdit.setModel(model)
lineEdit.setModelColumn(0)
lineEdit.show()
sys.exit(app.exec_())
This compiles fine, but no longer shows the QCompleter
if __name__ == '__main__':
class Example(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.centralWidget = QtGui.QWidget(self)
self.layout = QtGui.QVBoxLayout(self.centralWidget)
# can I push this inside the LineEdit class instead?
model = QtGui.QStandardItemModel()
for i, word in enumerate(['test', 'blah', 'heh', 'yep', 'hello', 'hi']):
item = QtGui.QStandardItem(word)
model.setItem(i, 0, item)
# Make a LineEdit instance
self.lineEdit = LineEdit(parent=self.centralWidget)
self.lineEdit.setModel(model)
self.lineEdit.setModelColumn(0)
self.layout.addWidget(self.lineEdit)
self.setCentralWidget(self.centralWidget)
app = QtGui.QApplication(sys.argv)
QtWin = Example()
QtWin.show()
sys.exit(app.exec_())
turned out to be quite simple really, hopefully this will help anyone else using PyQt's QCompleter for auto-completion
import sys
from PyQt4 import QtCore, QtGui
class LineEdit(QtGui.QLineEdit):
def __init__(self, parent, completerContents):
super(LineEdit, self).__init__(parent)
self.completerList = QtCore.QStringList()
for content in completerContents:
self.completerList.append(QtCore.QString(content))
self.completer = QtGui.QCompleter(self.completerList, self)
self.completer.setCompletionMode(QtGui.QCompleter.PopupCompletion)
self.completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.setCompleter(self.completer)
if __name__ == '__main__':
class Example(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.centralWidget = QtGui.QWidget(self)
self.layout = QtGui.QVBoxLayout(self.centralWidget)
# Example LineEdit Call
self.lineEdit = LineEdit(parent=self.centralWidget, completerContents=('test', 'blah', 'heh', 'yep', 'hello', 'hi'))
self.layout.addWidget(self.lineEdit)
self.setCentralWidget(self.centralWidget)
app = QtGui.QApplication(sys.argv)
QtWin = Example()
QtWin.show()
sys.exit(app.exec_())
There is possible 2 reasons of such a behavior in the second case:
Your completer has no completion model in the second case
Your LineEdit has set other completer
Don't know if U can to debug this and set breakpoint on QLineEdit::setCompleter in python.

Resources