I made a ui for work using PyQt5 and Python3. Additionally to clicking the buttons, I want to execute specific actions by pressing specific keys on my keyboard e.g. the space bar. I used the following to do so:
def keyPressEvent(self, event):
key = event.key()
if key == Qt.Key_Space:
print('space')
elif key == Qt.Key_W:
print('w')
Instead of printing 'space', it presses the focused button in the ui, when I'm pressing the space bar. Just like I hit return. Although pressing the 'W'-key prints 'w' as expected.
I already searched here at stackoverflow and other where in the web as well, but I found nothing really helpfull. The best I got was this one here. But it's using PyQt4.3 and copy and paste the code to my editor just brought me some errors. I thought the approach was a good one, but I were not able to transfer this to PyQt5.
Try it:
#from PyQt4.QtCore import *
#from PyQt4.QtGui import *
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
# create objects
self.la = QLabel("Press Space or `w` in this box:")
self.le = MyLineEdit()
self.la2 = QLabel("\nLook here:")
self.le2 = QLineEdit()
self.btnSpace = QPushButton("Button1")
self.btnSpace.setEnabled(False)
self.btnSpace.clicked.connect(self.onBtnSpace)
self.btnW = QPushButton("Button2")
self.btnW.setEnabled(False)
self.btnW.clicked.connect(self.onBtnW)
# layout
layout = QVBoxLayout()
layout.addWidget(self.la)
layout.addWidget(self.le)
layout.addWidget(self.la2)
layout.addWidget(self.le2)
layout.addWidget(self.btnSpace)
layout.addWidget(self.btnW)
self.setLayout(layout)
# connections
#self.connect(self.le, SIGNAL("tabPressed"), self.update)
self.le.signalTabPressed[str].connect(self.update)
def update(self, keyPressed):
newtext = str(self.le2.text()) + str(keyPressed) #"tab pressed "
self.le2.setText(newtext)
if keyPressed == "Key Space; ":
self.btnSpace.setEnabled(True)
self.btnSpace.setText(str(keyPressed))
if keyPressed == "Key W; ":
self.btnW.setEnabled(True)
self.btnW.setText(str(keyPressed))
def onBtnSpace(self):
print("keyPressed -> btnSpace")
def onBtnW(self):
print("keyPressed -> btnW")
class MyLineEdit(QLineEdit):
signalTabPressed = pyqtSignal(str) # +++
def __init__(self, *args):
QLineEdit.__init__(self, *args)
def event(self, event):
#if (event.type()==QEvent.KeyPress) and (event.key()==Qt.Key_Tab):
if (event.type()==QEvent.KeyPress) and (event.key()==Qt.Key_Space):
self.signalTabPressed.emit("Key Space; ")
return True
if (event.type()==QEvent.KeyPress) and (event.key()==Qt.Key_W):
self.signalTabPressed.emit("Key W; ")
return True
return QLineEdit.event(self, event)
def main():
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
With S. Nick's very useful answer I were able to come to a solution that really fits my needs.
class MyPushButton(QPushButton):
def __init__(self, *args):
QPushButton.__init__(self, *args)
def event(self, event):
if (event.type() == QEvent.KeyPress) and (event.key() == Qt.Key_Space):
print(foo)
return True
return QPushButton.event(self, event)
So, that's it. Whenever I press the space key on my keyboard now it just prints foo and nothing else. Where print(foo) is just a place-holder for whatever I wish to do, obviously.
Related
I'm new to this so be patient.(english is not my main language so if there are typos then I'm sorry.)
I want to get the gui to start and load before the function starts.
Program Showcase of the problem
The problem I have is that the function starts in the console first and then I have to stop it before the gui can start, after I have done all that then everything works as it should.
Edit:
the options panel in the main window picture gets the text from a method inside the functions file with a print statement and is therefore not interactible, that's why I have an input function in the optionsPicker() just to clarify.
the gui code:
import sys
import io
from PyQt5.uic import loadUi
from PyQt5 import *
from PyQt5 import QtGui,QtCore, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class WelcomeScreen(QDialog):
def __init__(self):
super(WelcomeScreen, self).__init__()
loadUi("welcome.ui", self)
self.continueButton.clicked.connect(self.goToMenu)
def goToMenu(self):
mainMenu = MainMenuScreen()
widget.addWidget(mainMenu)
widget.setCurrentIndex(widget.currentIndex()+1)
class MainMenuScreen(QDialog):
def __init__(self):
super(MainMenuScreen, self).__init__()
loadUi("mainmenu.ui", self)
self.setOptionsField()
self.out = self.output
self.inBar = self.inputBox
self.process = QtCore.QProcess(self)
self.process.setProgram(sys.executable)
self.process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)
self.process.readyReadStandardError.connect(self.on_readyReadStandardError)
self.inBar.editingFinished.connect(self.on_editingFinished)
def runFile(self, url):
self.process.setArguments([url])
self.process.start()
def setOptionsField(self):
self.options.setText(getMethods())
#QtCore.pyqtSlot()
def on_readyReadStandardOutput(self):
out = self.process.readAllStandardOutput().data().decode()
self.out.insertPlainText(out)
self.output.moveCursor(QtGui.QTextCursor.End)
#QtCore.pyqtSlot()
def on_readyReadStandardError(self):
err = self.process.readAllStandardError().data().decode()
self.out.insertPlainText("\n" + err)
self.output.moveCursor(QtGui.QTextCursor.End)
#QtCore.pyqtSlot()
def on_editingFinished(self):
self.process.write(self.inBar.text().encode() + b"\n")
self.inBar.clear()
def keyPressEvent(self, event):
if event.key() == Qt.Key_F5:
self.output.clear()
if event.key() == Qt.Key_Escape:
sys.exit()
if __name__ == "__main__":
app = QApplication(sys.argv)
welcome = WelcomeScreen()
window = MainMenuScreen()
window.runFile("Assignments_2.py") #reads and executes the function
widget = QStackedWidget()
widget.addWidget(welcome)
widget.addWidget(window)
widget.setFixedHeight(800)
widget.setFixedWidth(1200)
widget.show()
sys.exit(app.exec_())
this is the function it's calling from a different .py file:
def optionsPicker():
while True:
options = input("\nPick an option: ")
match options:
case "0":
stringMethods()
case "1":
randomNumbers()
case "2":
distanceBetweenPoints()
case "3":
distanceBetweenPointsDialog()
case "4":
calcVolOfSphere()
case "5":
roundOffAndCut()
case "exit":
break
case "":
print("Invalid Option! Try again.")
continue
optionsPicker()
pictures of how the gui looks:
welcome screen
main window
Grееtings аll. I am new to this site, so go easy on me.
I am building a program in python using PyQt5 for the interface. I currently have, among other things, a QListWidget (list window) into which I insert a number of QWidgets (list items) through a function during the running of the program. I have implemented an eventFilter by subclassing QObject, and I use it to differentiate between left and right click, and from that I send to the controller class to handle one or the other click accordingly.
I have made it so that when right-clicked on a list item, a context menu appears. However, I also need a context menu to appear when the list window is clicked (not on a list item). The problem which occurs is that when right-clicked on a list item, both the list item context menu and list window context menu appear. This must be because the event filter recognises the click as occurring within the list window, because it is occurring on a list item, which is within the list window. What I need is that when right-clicked on a list item, only its context menu appears, and similarly for the list window, when right-clicked outside the list items.
I have tried checking if source equals the widget where the event appeared, but it seems to recognise both widgets' events independently and if I gate the call to the handler with an if condition, one of the handlers never receives a call. I have searched around the web and this site, but I have found nothing of use. Perhaps it is due to me not being a native speaker and so not being able to phrase things correctly (you can see how clunky my phrasing is).
Below follows some code extracted from my program. Note that anything irrelevant has been cut away to make for a minimal example. For your convenience, I have also merged the GUI files into one and did the same for the control files. I have tested this minimal example and it reproduces the problem. It could not get smaller, so if you deem the code listed below too long, notify me and I can reupload it to GitHub if it is allowed to show the minimal example that way instead of putting code into the question directly.
custom_classes.py:
from PyQt5.QtCore import Qt, QEvent, QObject
class MyFilter(QObject):
def __init__(self, parent, ctrl):
super().__init__(parent)
self._parent = parent
self.ctrl = ctrl
self.parent.installEventFilter(self)
#property
def parent(self):
return self._parent
def eventFilter(self, source, event):
if event.type() == QEvent.MouseButtonPress:
if event.button() == Qt.LeftButton:
self.ctrl.handle_left_click()
elif event.button() == Qt.RightButton:
self.ctrl.handle_right_click(event)
return super().eventFilter(source, event)
gui.py:
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QLabel
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QListWidget
from PyQt5.QtWidgets import QScrollArea
from PyQt5.QtCore import Qt
class MainFrame(QWidget):
def __init__(self):
super().__init__()
self.main_layout = QHBoxLayout()
self.setLayout(self.main_layout)
class ListItemFrame(QWidget):
def __init__(self):
super().__init__()
self.main = QHBoxLayout()
self.main.setContentsMargins(0,0,0,0)
self.name_layout = QHBoxLayout()
self.name_layout.setContentsMargins(0,0,0,0)
self.main.addLayout(self.name_layout)
self.name = QLabel("")
self.name.setMaximumHeight(20)
self.name_layout.addWidget(self.name)
self.setLayout(self.main)
class ListFrame(QListWidget):
def __init__(self):
super().__init__()
self.main = QVBoxLayout()
self.scroll_widget = QScrollArea()
self.scroll_widget.setWidgetResizable(True)
self.scroll_layout = QVBoxLayout()
self.scroll_layout.setAlignment(Qt.AlignTop)
self.scroll_layout_widget = QWidget()
self.scroll_layout_widget.setLayout(self.scroll_layout)
self.scroll_widget.setWidget(self.scroll_layout_widget)
self.main.addWidget(self.scroll_widget)
self.setLayout(self.main)
ctrl.py:
from PyQt5.QtWidgets import QMenu
from gui import ListFrame, ListItemFrame
from custom_classes import MyFilter
class Controller:
def __init__(self, ui, app):
self.ui = ui
self.app = app
self.list_ = ListControl(self)
class ListControl:
def __init__(self, ctrl):
self.ctrl = ctrl
self.ui = ListFrame()
self.the_list = self.get_list() #list of stuff
self.item_list = [] #list of list items
self.ctrl.ui.main_page.main_layout.addWidget(self.ui)
self.index = self.ctrl.ui.main_page.main_layout.count() - 1
self.filter = MyFilter(self.ui, self)
self.show_list()
def handle_left_click(self):
pass #other irrelevant function
def handle_right_click(self, event):
self.show_options(event)
def show_options(self, event):
menu = QMenu()
one_action = menu.addAction("Something!")
quit_action = menu.addAction("Quit")
action = menu.exec_(self.ui.mapToGlobal(event.pos()))
if action == quit_action:
self.ctrl.ui.close()
elif action == one_action:
self.something()
def something(self):
print("Something!")
def show_list(self):
for info in self.the_list:
item = ListItem(self, info)
self.item_list.append(item)
def get_list(self):
return [x for x in "qwertzuiopasdfghjklyxcvbnm"]
class ListItem:
def __init__(self, main, info):
self.main = main
self.info = info*10
self.ui = ListItemFrame()
self.filter = MyFilter(self.ui, self)
self.set_ui()
self.add_to_ui()
self.main.ui.scroll_layout.addWidget(self.ui)
def handle_left_click(self):
pass #other irrelevant function
def handle_right_click(self, event):
self.show_options(event)
def show_options(self, event):
menu = QMenu()
item_action = menu.addAction("Hello!")
quit_action = menu.addAction("Quit")
action = menu.exec_(self.ui.mapToGlobal(event.pos()))
if action == quit_action:
self.main.ctrl.ui.close()
elif action == item_action:
self.hello()
def hello(self):
print(f"Hello! I am {self.info}")
def set_ui(self):
self.ui.name.setText(self.info)
def add_to_ui(self):
self.main.ui.scroll_layout.insertWidget(
self.main.ui.scroll_layout.count() - 1, self.ui
)
main.py:
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QStackedLayout
from PyQt5.QtWidgets import QWidget
from gui import MainFrame
from ctrl import Controller
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("minimal example")
self.stacked = QStackedLayout()
self.main_page = MainFrame()
self.stacked.addWidget(self.main_page)
self.setLayout(self.stacked)
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setStyle("Fusion")
window = Window()
window.show()
c = Controller(window, app)
sys.exit(app.exec())
To reiterate, the context menu appears for both the list item and the list window when a list item is right-clicked. What I need is for it to appear only for the list item if a list item is right-clicked.
Edit: seems the site bit off a part of my introduction. Readded it!
this is probably not the best way to do it but it works. You just create a global variable, for example list_element_clicked and when you click "hello" (of course not quit because you are going to exit the window and there is no point) you set that variable to True. If that variable is False, you show the ListControl context menu, and if not, you set that variable to True, so next time if you click on your ListControl it will appear, and if you click on ListItem it will not.
Finally there is an extra case, if you don't click anywhere after clicking on ListItem, nothing will happen (the ListControl is not shown and the variable is not changed) so everything will work perfectly next time.
So here is the code:
ctrl.py:
from PyQt5.QtWidgets import QMenu
from gui import ListFrame, ListItemFrame
from custom_classes import MyFilter
list_element_clicked = False
class Controller:
def __init__(self, ui, app):
self.ui = ui
self.app = app
self.list_ = ListControl(self)
class ListControl:
def __init__(self, ctrl):
self.ctrl = ctrl
self.ui = ListFrame()
self.the_list = self.get_list() #list of stuff
self.item_list = [] #list of list items
self.ctrl.ui.main_page.main_layout.addWidget(self.ui)
self.index = self.ctrl.ui.main_page.main_layout.count() - 1
self.filter = MyFilter(self.ui, self)
self.show_list()
def handle_left_click(self):
pass #other irrelevant function
def handle_right_click(self, event):
global list_element_clicked
if(list_element_clicked == False):
self.show_options(event)
else:
list_element_clicked = False
def show_options(self, event):
menu = QMenu()
one_action = menu.addAction("Something!")
quit_action = menu.addAction("Quit")
action = menu.exec_(self.ui.mapToGlobal(event.pos()))
if action == quit_action:
self.ctrl.ui.close()
elif action == one_action:
self.something()
def something(self):
print("Something!")
def show_list(self):
for info in self.the_list:
item = ListItem(self, info)
self.item_list.append(item)
def get_list(self):
return [x for x in "qwertzuiopasdfghjklyxcvbnm"]
class ListItem:
def __init__(self, main, info):
self.main = main
self.info = info*10
self.ui = ListItemFrame()
self.filter = MyFilter(self.ui, self)
self.set_ui()
self.add_to_ui()
self.main.ui.scroll_layout.addWidget(self.ui)
def handle_left_click(self):
pass #other irrelevant function
def handle_right_click(self, event):
self.show_options(event)
def show_options(self, event):
menu = QMenu()
item_action = menu.addAction("Hello!")
quit_action = menu.addAction("Quit")
action = menu.exec_(self.ui.mapToGlobal(event.pos()))
if action == quit_action:
self.main.ctrl.ui.close()
elif action == item_action:
global list_element_clicked
list_element_clicked = True
self.hello()
def hello(self):
print(f"Hello! I am {self.info}")
def set_ui(self):
self.ui.name.setText(self.info)
def add_to_ui(self):
self.main.ui.scroll_layout.insertWidget(
self.main.ui.scroll_layout.count() - 1, self.ui
)
I have a function named generate_input_event. I'm trying to use this function to simulate a keypress within a QWebEngineView.
def generate_input_event(window_id, key_code, modifiers, low_level_data, x, y):
modifiers_flag = create_modifiers_flag(modifiers)
logging.info("generate input, window: {} code: {}, modifiers {}".format(
window_id, key_code, modifiers_flag))
event = QKeyEvent(QEvent.KeyPress, key_code, modifiers_flag)
event.artificial = True
event_window = window.get_window(window_id)
QCoreApplication.sendEvent(event_window.qtwindow, event)
Whenever I run my program and highlight an input field within my QWebEngineView, and invoke generate_input_event it is expected that it will type that letter into the input field.
I also set-up an event filter to capture everything all key presses EXCEPT for my artificially generated ones.
class EventFilter(QWidget):
def __init__(self, parent=None):
super(EventFilter, self).__init__(parent)
qApp.installEventFilter(self)
def eventFilter(self, obj, event):
if (event.type() == QEvent.KeyPress and hasattr(event, 'artificial')):
logging.info("artificial event")
return False. # send to widget
elif (event.type() == QEvent.KeyPress and not is_modifier(event.key())):
modifiers = create_modifiers_list(event.modifiers())
key_string = create_key_string(event)
key_code = event.key()
logging.info("send code: {} string: {} modifiers {}".format(
key_code, key_string, modifiers))
return True. # do not forward to widgets
return False
However when I actually run my code, this is the following output I get:
INFO:root:send code: 65 string: a modifiers ['']
INFO:root:generate input, window: 1 code: 65, modifiers <PyQt5.QtCore.Qt.KeyboardModifiers object at 0x106a4ea58>
INFO:root:artificial event
The output looks correct, HOWEVER, the input field of the QWebEngineView never actually gets a letter that was artificially generated by generate_input_event.
P.S. Should you wish to see the whole of the file/project for reasons of context, please look at this branch/file here: https://github.com/atlas-engineer/next/blob/generate_events/ports/pyqt-webengine/utility.py
Qt Webengine uses RenderWidgetHostViewQtDelegateWidget to render and this is created after loading a page so you must access it after load() or setHtml(), so that widget must send those events.
The following example will show a QWebEngineView and a QLineEdit, after showing both windows what you type in the QLineEdit will be shown in the QWebEngineView.
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
class EventFilter(QtCore.QObject):
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.KeyPress and hasattr(
event, "artificial"
):
print("event:", event.key(), event.text())
return False
return super().eventFilter(obj, event)
class ForwardKeyEvent(QtCore.QObject):
def __init__(self, sender, receiver, parent=None):
super(ForwardKeyEvent, self).__init__(parent)
self.m_sender = sender
self.m_receiver = receiver
self.m_sender.installEventFilter(self)
def eventFilter(self, obj, event):
if self.m_sender is obj and event.type() == QtCore.QEvent.KeyPress:
# self.m_receiver.setFocus()
new_event = QtGui.QKeyEvent(
QtCore.QEvent.KeyPress,
event.key(),
event.modifiers(),
event.text(),
)
new_event.artificial = True
QtCore.QCoreApplication.postEvent(self.m_receiver, new_event)
return super().eventFilter(obj, event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ef = EventFilter()
app.installEventFilter(ef)
lineedit = QtWidgets.QLineEdit()
lineedit.show()
view = QtWebEngineWidgets.QWebEngineView()
view.resize(640, 480)
view.show()
view.load(QtCore.QUrl("https://www.google.com/"))
# RenderWidgetHostViewQtDelegateWidget is created after loading a page
# so you must access it after load() or setHtml().
render_widget = view.findChild(QtWidgets.QWidget)
print(render_widget.metaObject().className())
assert(render_widget)
fe = ForwardKeyEvent(lineedit, render_widget)
sys.exit(app.exec_())
The correct way to post and listen to events for a QWebengineview can be demonstrated with the following minimal example:
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
from PyQt5.QtCore import Qt
class ForwardKeyEvent(QtCore.QObject):
def __init__(self, sender, receiver, parent=None):
super(ForwardKeyEvent, self).__init__(parent)
self.m_sender = sender
self.m_receiver = receiver
self.m_sender.installEventFilter(self)
def eventFilter(self, obj, event):
if self.m_sender is obj and event.type() == QtCore.QEvent.KeyPress:
new_event = QtGui.QKeyEvent(
QtCore.QEvent.KeyPress,
65,
Qt.KeyboardModifiers(),
"a",
)
new_event.artificial = True
QtCore.QCoreApplication.postEvent(self.m_receiver.focusProxy(), new_event)
return True
return False
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
lineedit = QtWidgets.QLineEdit()
lineedit.show()
view = QtWebEngineWidgets.QWebEngineView()
view.resize(640, 480)
view.show()
view.load(QtCore.QUrl("https://www.google.com/"))
# RenderWidgetHostViewQtDelegateWidget is created after loading a page
# so you must access it after load() or setHtml().
fe = ForwardKeyEvent(lineedit, view)
sys.exit(app.exec_())
I am using QTableWidget to creating something like excel. One of the QTableWidget column is able to let user update note with multiple line. Before use QTextEdit, user need to manually add " \n" to achieve multiple line, but this is not friendly user. I found out I can set QTextEdit into QTableWidget. By using QTextEdit, I able to type multiple line by press "Enter" or "Shift+Enter". However, I want when "Shift+Enter" is pressed, it go to next line but when "Enter" is pressed, it run self.update_MySQL function.
Below is my sample code
import sys, itertools, sip
sip.setapi('QVariant',2)
from PyQt4 import QtCore, QtGui
class CustomTextEditDelegate(QtGui.QItemDelegate):
def createEditor(self, parent, option, index):
editor = QtGui.QTextEdit(parent)
return editor
def setEditorData(self, editor, index):
editor.setText(index.data())
def setModelData(self, editor, model, index):
model.setData(index, editor.toPlainText())
class PIX_DATABASE_UI(QtGui.QTableWidget):
def __init__(self, parent=None):
super(PIX_DATABASE_UI, self).__init__(parent)
### signal
self.update_tableWidget()
self.itemEntered.connect(self.update_MySQL)
# -----------------------------------------------------------------------------------------------------------------#
def update_MySQL(self):
print "MySQL Updated"
def update_tableWidget(self):
self.filter_columns = [u'remark']
self.setColumnCount(len(self.filter_columns))
self.setHorizontalHeaderLabels(self.filter_columns)
self.setRowCount(5)
for row, col in itertools.product(range(5), range(len(self.filter_columns))):
if self.filter_columns[col] == "remark":
width = self.sizeHint().width()
self.setColumnWidth(col, width * 0.75)
self.setItem(row, col, QtGui.QTableWidgetItem(str("ABC")))
self.setItemDelegateForColumn(col, CustomTextEditDelegate(self))
self.verticalHeader().setResizeMode(row, QtGui.QHeaderView.ResizeToContents)
# -----------------------------------------------------------------------------------------------------------------#
# -----------------------------------------------------------------------------------------------------------------#
if __name__ == '__main__':
global ui
try:
ui.close()
except:
pass
app = QtGui.QApplication(sys.argv)
app.setStyle(QtGui.QStyleFactory.create("Plastique"))
# print QtGui.QStyleFactory.keys()
ui = PIX_DATABASE_UI()
ui.show()
sys.exit(app.exec_())
Conclusion:
Thanks eyllanesc, the code is help me to achieve what I want to achieve with slightly modify to the keyPressEvent. Original code is still emit even I press "Shift + Enter".
Below code is what I modified.
def keyPressEvent(self, event):
modifiers = QtGui.QApplication.keyboardModifiers()
if modifiers != QtCore.Qt.ShiftModifier and event.key() == QtCore.Qt.Key_Return:
self.enter.emit()
# If you do not want a new line uncomment the following
# return
super(TextEdit, self).keyPressEvent(event)
So now, after edit in textEdit and press "Enter", it will run self.update_MySQL and when press "Shift + Enter", it will go to next line.
What you can do is that the data of the model is updated and for this the commitData signal calling the setModelData() method must be emited.
By doing this you can use the signal itemChanged() because the data of the item is modified.
import sys, itertools, sip
sip.setapi('QVariant',2)
from PyQt4 import QtCore, QtGui
class TextEdit(QtGui.QTextEdit):
pressed = QtCore.pyqtSignal()
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Return:
self.pressed.emit()
# If you do not want a new line uncomment the following
# return
super(TextEdit, self).keyPressEvent(event)
class CustomTextEditDelegate(QtGui.QItemDelegate):
def createEditor(self, parent, option, index):
editor = TextEdit(parent)
editor.pressed.connect(self.commitAndCloseEditor)
return editor
def setEditorData(self, editor, index):
editor.setText(index.data())
def setModelData(self, editor, model, index):
model.setData(index, editor.toPlainText())
def commitAndCloseEditor(self):
editor = self.sender()
self.commitData.emit(editor)
# if you want to close the editor uncomment the following
# self.closeEditor.emit(editor, QtGui.QAbstractItemDelegate.NoHint)
class PIX_DATABASE_UI(QtGui.QTableWidget):
def __init__(self, parent=None):
super(PIX_DATABASE_UI, self).__init__(parent)
### signal
self.update_tableWidget()
self.itemEntered.connect(self.update_MySQL)
self.itemChanged.connect(self.update_MySQL)
# -----------------------------------------------------------------------------------------------------------------#
def update_MySQL(self, it):
print("MySQL Updated", it.text())
def update_tableWidget(self):
self.filter_columns = [u'remark']
self.setColumnCount(len(self.filter_columns))
self.setHorizontalHeaderLabels(self.filter_columns)
self.setRowCount(5)
for row, col in itertools.product(range(5), range(len(self.filter_columns))):
if self.filter_columns[col] == "remark":
width = self.sizeHint().width()
self.setColumnWidth(col, width * 0.75)
self.setItem(row, col, QtGui.QTableWidgetItem(str("ABC")))
self.setItemDelegateForColumn(col, CustomTextEditDelegate(self))
self.verticalHeader().setResizeMode(row, QtGui.QHeaderView.ResizeToContents)
# -----------------------------------------------------------------------------------------------------------------#
# -----------------------------------------------------------------------------------------------------------------#
if __name__ == '__main__':
global ui
try:
ui.close()
except:
pass
app = QtGui.QApplication(sys.argv)
app.setStyle(QtGui.QStyleFactory.create("Plastique"))
# print QtGui.QStyleFactory.keys()
ui = PIX_DATABASE_UI()
ui.show()
sys.exit(app.exec_())
Using PyQt 4.8 and Python 3.3
I'm using a modified version of this example: whereas this example emits a signal on tab press and adds arbitrary text to the second QLineEdit, I want my script to emit a signal on any keypress, add arbitrary signal text to the 2nd QLineEdit, and add the typed character to the 1st QLineEdit (assuming it's a valid ASCII character).
Whenever I try to use any keypress as a signal, I can no longer grab that text to input into QLineEdit. Here's what I have so far and where I'm stuck:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
####################################################################
def main():
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
####################################################################
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
# create objects
self.la = QLabel("Type in this box:")
self.le = MyLineEdit()
self.la2 = QLabel("\nLook here:")
self.le2 = QLineEdit()
self.char = MyLineEdit.char # HOW CAN I GET THIS WORKING?
# layout
layout = QVBoxLayout()
layout.addWidget(self.la)
layout.addWidget(self.le)
layout.addWidget(self.la2)
layout.addWidget(self.le2)
self.setLayout(layout)
# connections
self.connect(self.le, SIGNAL("keyPressed"),
self.update)
def update(self):
newtext1 = self.le.text() + self.char
newtext2 = self.le2.text() + "kP "
self.le.setText(newtext1)
self.le2.setText(newtext2)
####################################################################
class MyLineEdit(QLineEdit):
def __init__(self, *args):
QLineEdit.__init__(self, *args)
def event(self, event):
if (event.type() == QEvent.KeyPress):
self.emit(SIGNAL("keyPressed"))
self.char = "%c" % (event.key())
return True
return QLineEdit.event(self, event)
####################################################################
if __name__ == "__main__":
main()
Any and all help is greatly appreciated. Is there something within PyQt4 that allows me to use a keypress as both a signal and input text, or is my Python off?
Problem1: you are emitting the signal before setting self.char:
class MyLineEdit(QLineEdit):
def __init__(self, *args):
QLineEdit.__init__(self, *args)
self.char = ""
def event(self, event):
if (event.type() == QEvent.KeyPress):
self.char = "%c" % (event.key()) #this line above the next
self.emit(SIGNAL("keyPressed"))
return True
return QLineEdit.event(self, event)
Problem 2: use the char value in your MyLineEdit object:
def update(self):
newtext1 = self.le.text() + self.le.char
newtext2 = self.le2.text() + "kP "
self.le.setText(newtext1)
self.le2.setText(newtext2)
Finally you don't need self.char on MyWindow