Why keyPress Event in PyQt does not work for key Enter? - events

Why, when I press Enter, does the keyPressEvent method not do what I need? It just moves the cursor to a new line.
class TextArea(QTextEdit):
def __init__(self, parent):
super().__init__(parent=parent)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.show()
def SLOT_SendMsg(self):
return lambda: self.get_and_send()
def get_and_send(self):
text = self.toPlainText()
self.clear()
get_connect(text)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Enter:
self.get_and_send()
else:
super().keyPressEvent(event)

Qt.Key_Enter is the Enter located on the keypad:
Qt::Key_Return 0x01000004
Qt::Key_Enter 0x01000005 Typically located on the keypad.
Use:
def keyPressEvent(self, qKeyEvent):
print(qKeyEvent.key())
if qKeyEvent.key() == QtCore.Qt.Key_Return:
print('Enter pressed')
else:
super().keyPressEvent(qKeyEvent)

def keyPressEvent(self, event):
if (event.key() == 16777220) or (event.key() == 43): # for main keyboard and keypad
Works for my keyboard.

My grain of salt to complement the answers from #warvariuc and #tCot. A bit more pythonic:
def keyPressEvent(self, qKeyEvent):
if qKeyEvent.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
pass # your code
else:
super().keyPressEvent(qKeyEvent)

Related

Qt6: how to disable selection for empty cells in QTableView?

I'm trying to display some data from a database in a grid view, similar to how a file manager works. I thought of using a QTableView as the grid view since it did what I wanted out of the box. However, as shown with the below given MRE, even if just a single cell has value, you can still select the other empty cells, how can I prevent this? Basically, I want to make it so that only cells with a value can be selected.
MRE:
from PySide6 import QtWidgets as qtw
from PySide6 import QtGui as qtg
from PySide6 import QtCore as qtc
ROW_COUNT = 5
COL_COUNT = 5
class Model(qtc.QAbstractTableModel):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self._data = [[None for _ in range(COL_COUNT)] for _ in range(ROW_COUNT)]
def data(self, index: qtc.QModelIndex, role: qtc.Qt.ItemDataRole):
if not index.isValid():
return None
if role == qtc.Qt.ItemDataRole.DisplayRole:
return self._data[index.row()][index.column()]
return None
def setData(self, index: qtc.QModelIndex, value, role: qtc.Qt.ItemDataRole=qtc.Qt.ItemDataRole.DisplayRole):
if not index.isValid():
return False
if role == qtc.Qt.ItemDataRole.DisplayRole:
self._data[index.row()][index.column()] = value
return False
def rowCount(self, _):
return ROW_COUNT
def columnCount(self, _):
return COL_COUNT
app = qtw.QApplication()
view = qtw.QTableView()
view.setModel(Model())
view.setShowGrid(False)
view.verticalHeader().setVisible(False)
view.horizontalHeader().setVisible(False)
view.model().setData(view.model().createIndex(0, 0), "this is a test")
view.show()
app.exec()
You need to override the flags() and ensure that it doesn't return the ItemIsSelectable flag.
class Model(qtc.QAbstractTableModel):
# ...
def flags(self, index):
flags = super().flags(index)
if index.data() is None:
flags &= ~qtc.Qt.ItemIsSelectable
return flags
In your case, you also probably want to avoid the ItemIsEnabled, and since these two flags are the default one, you can just return NoItemFlags
def flags(self, index):
if index.data() is None:
return qtc.Qt.NoItemFlags
return super().flags(index)
If you also need to clear the selection, then you could subclass the view and do it in the mousePressEvent():
class TableView(qtw.QTableView):
def mousePressEvent(self, event):
index = self.indexAt(event.pos())
if index.isValid() and not index.flags() & qtc.Qt.ItemIsSelectable:
self.clearSelection()
else:
super().mousePressEvent(event)

Tkinter auto scrolling frame almost working. Apreciate input

I am trying to develop a scrollable frame in tkinter that can be used in the same way a normal frame can.
Thanks to many hint in this forum i develloped some code, that does exactly what it is supposed to, if i pack the scrollframe in the root window.
Unfortunately it fails if i use place or grid.
Here the code for the Frame
import tkinter as tk
class ScrollFrame(tk.Frame): #this frame will be placed on a canvas that is in a frame that goes on the parent window
class AutoScrollbar(tk.Scrollbar):
def set(self, *args):
if float(args[0])==0 and float(args[1])==1: self.grid_forget()
else:
if self.cget('orient')=="vertical": self.grid(row=0,column=1,sticky="ns")
else: self.grid(row=1,column=0,sticky="ew")
tk.Scrollbar.set(self, *args)
def __init__(self, root,*args,**args2):
self.outer_frame=tk.Frame(root,*args,**args2) #this is the frame that will be packed in th parent window
self.outer_frame.grid_columnconfigure(0, weight=1)
self.outer_frame.grid_rowconfigure(0, weight=2)
self.canvas = tk.Canvas(self.outer_frame, borderwidth=0, background="#ffffff")
tk.Frame.__init__(self, self.canvas,*args,**args2)
self.vscroll = ScrollFrame.AutoScrollbar(self.outer_frame, orient="vertical", command=self.canvas.yview)
self.hscroll = ScrollFrame.AutoScrollbar(self.outer_frame, orient="horizontal", command=self.canvas.xview)
self.canvas.configure(yscrollcommand=self.vscroll.set, xscrollcommand=self.hscroll.set)
self.canvas.create_window((0,0), window=self, anchor="nw")
self.canvas.grid(row=0,column=0,sticky="news")
self.hscroll.grid(row=1,column=0,sticky="ew")
self.vscroll.grid(row=0,column=1,sticky="ns")
self.bind("<Configure>", self.onFrameConfigure)
def onFrameConfigure(self, event): #Adapt the scroll region #does the resizing
self.canvas.config(scrollregion=self.canvas.bbox("all"))
self.canvas.config(width=event.width, height=event.height)
#convenience functions so the ScrollFrame can be treated like a normal frame
def destr_org(self):tk.Frame.destroy(self)
def destroy(self):
self.destroy=self.destr_org
self.outer_frame.destroy()
def pack(self,*args,**arg2):
self.outer_frame.pack(*args,**arg2)
def place(self,*args,**arg2):
self.outer_frame.place(*args,**arg2)
def grid(self,*args,**arg2):
self.outer_frame.grid(*args,**arg2)
def pack_forget(self,*args,**arg2):
self.outer_frame.pack_forget(*args,**arg2)
def place_forget(self,*args,**arg2):
self.outer_frame.place_forget(*args,**arg2)
def grid_forget(self,*args,**arg2):
self.outer_frame.grid_forget(*args,**arg2)
def config(self,*args,**arg2):
self.outer_frame.config(*args,**arg2)
tk.Frame.config(self,*args,**arg2)
def configure(self,*args,**arg2):
self.outer_frame.config(*args,**arg2)
tk.Frame.config(self,*args,**arg2)
here the code i used to test it. Just uncomment the f.place and f.grid lines to try them.
win=tk.Tk()
f=ScrollFrame(win)
for n in range(10):
o=tk.Button(f,text="-----------------------"+str(n)+"------------------------")
o.pack()
f.pack(expand=True, fill=tk.BOTH)
#f.place(x=0, y=0)
#f.grid(column=0,row=0)
Since i get no errors i am somewat lost and would be grateful for hints why it doesnt work.
I know there are packages with scrollable frames, but i really would like to get a frame without additional imports.
Its also tru that it is a little more complicated than necessary, but that is because I tried to design it in a way that it can be filled and placed exactly like a tk.Frame
Thanks a lot
Ok, I figured it out.
This works quite well for pack() and place() (didnt try to get grid working) (on Linux)
The key for place() is the binding to the resizing of the parent window, whereas for pack() the resizing of the inner frame (=self) seems to be important.
Even though the code of Novel (see comments above) is quite more elegant than mine, it sometimes gave me errors were this one worked :-)
class ScrollFrame(tk.Frame):
class AutoScrollbar(tk.Scrollbar):
def set(self, *args):
if float(args[0])==0 and float(args[1])==1: self.grid_forget()
else:
if self.cget('orient')=="vertical": self.grid(row=0,column=1,sticky="ns")
else: self.grid(row=1,column=0,sticky="ew")
tk.Scrollbar.set(self, *args)
def __init__(self, root,*args,**args2):
self.rootwin=root
self.dir=tk.BOTH
if "dir" in args2:
self.dir=args2["dir"]
del(args2["dir"])
self.outer_frame=tk.Frame(root,*args,**args2)
self.outer_frame.grid_columnconfigure(0, weight=1)
self.outer_frame.grid_rowconfigure(0, weight=2)
self.canvas = tk.Canvas(self.outer_frame, borderwidth=0, background="#ffffff")
tk.Frame.__init__(self, self.canvas,*args,**args2)
if self.dir==tk.Y or self.dir==tk.BOTH :
self.vscroll = ScrollFrame.AutoScrollbar(self.outer_frame, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vscroll.set)
self.vscroll.grid(row=0,column=1,sticky="ns")
if self.dir==tk.X or self.dir==tk.BOTH :
self.hscroll = ScrollFrame.AutoScrollbar(self.outer_frame, orient="horizontal", command=self.canvas.xview)
self.canvas.configure(xscrollcommand=self.hscroll.set)
self.hscroll.grid(row=1,column=0,sticky="ew")
self.canvas.create_window((0,0), window=self, anchor="nw")
self.canvas.grid(row=0,column=0,sticky="news")
self.canvas.bind("<Enter>", self._bind_mouse)
self.canvas.bind("<Leave>", self._unbind_mouse)
def onFrameConfigure(self, event): #Adapt the scroll region
bb=self.canvas.bbox("all")
self.canvas.config(scrollregion=bb)
self.canvas.config(height=event.height,width=event.width)
def onRootConf(self, event):
bb=self.canvas.bbox("all")
self.canvas.config(scrollregion=bb)
w=bb[2]-bb[0]
h=bb[3]-bb[1]
rw=self.rootwin.winfo_width()-self.outer_frame.winfo_x()-20*int(self.vscroll.winfo_ismapped())
rh=self.rootwin.winfo_height()-self.outer_frame.winfo_y()-20*int(self.hscroll.winfo_ismapped())
if rh<h and (self.dir==tk.Y or self.dir==tk.BOTH):
h=rh
if rw<w and (self.dir==tk.X or self.dir==tk.BOTH):
w=rw
self.canvas.config(height=h,width=w)
def destr_org(self):tk.Frame.destroy(self)
def destroy(self):
self.destroy=self.destr_org
self.outer_frame.destroy()
def pack(self,*args,**arg2):
self.bind("<Configure>", self.onFrameConfigure)
self.outer_frame.pack(*args,**arg2)
def place(self,*args,**arg2):
self.outer_frame.place(*args,**arg2)
self.bind("<Configure>",self.onRootConf)
self.rootwin.bind("<Configure>",self.onRootConf)
def grid(self,*args,**arg2):
self.outer_frame.grid(*args,**arg2)
def pack_forget(self,*args,**arg2):
self.outer_frame.pack_forget(*args,**arg2)
def place_forget(self,*args,**arg2):
self.outer_frame.place_forget(*args,**arg2)
def grid_forget(self,*args,**arg2):
self.outer_frame.grid_forget(*args,**arg2)
def config(self,*args,**arg2):
self.outer_frame.config(*args,**arg2)
tk.Frame.config(self,*args,**arg2)
def configure(self,*args,**arg2):
self.outer_frame.config(*args,**arg2)
tk.Frame.config(self,*args,**arg2)
def winfo_ismapped(self):
return self.outer_frame.winfo_ismapped()
def _bind_mouse(self, event=None):
self.canvas.bind_all("<4>", self._on_mousewheel)
self.canvas.bind_all("<5>", self._on_mousewheel)
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
def _unbind_mouse(self, event=None):
self.canvas.unbind_all("<4>")
self.canvas.unbind_all("<5>")
self.canvas.unbind_all("<MouseWheel>")
def _on_mousewheel(self, event):
if event.num == 4 or event.delta == 120: self.canvas.yview_scroll(-1, "units" )
elif event.num == 5 or event.delta == -120: self.canvas.yview_scroll(1, "units" )

PyQt how to capture print output and display in text field

The following script is part of a more complex one. I have taken out some parts just for simplification.
My aim is to insert a textedit field that captures the print output of the command window (when the pushbutton is pressed) and displays it inside the text field while the program is executing.
The suggestions I found are too much aimed at scripts that have no other functions. But my script is already quite complex and I don’t want to change it from the beginning or rewrite the whole script.
Does anyone have an idea on how to include the function in the script in a relatively simple way? I have tried it without success.
Any kind help would be appreciated.
import sys
import subprocess
from PyQt4 import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.win_widget = WinWidget(self)
widget = QtGui.QWidget()
layout = QtGui.QVBoxLayout(widget)
layout.addWidget(self.win_widget)
self.setCentralWidget(widget)
self.statusBar().showMessage('Ready')
self.setGeometry(300, 300, 450, 250)
self.setWindowTitle('capture PyQt output')
self.setWindowIcon (QtGui.QIcon('logo.png'))
self.show()
class WinWidget (QtGui.QWidget) :
def __init__(self, parent):
super (WinWidget , self).__init__(parent)
self.controls()
self.grid_layout()
self.capture_output()
def controls(self):
self.btn_newSearch = QtGui.QPushButton('capture PyQt output', self)
self.btn_newSearch.clicked.connect(self.some_funtion)
self.btn_newSearch.setFont(QtGui.QFont('CourierNew', 12 , QtGui.QFont.Bold,False))
def capture_output (self) :
# HERE I WANT TO PUT A (IF POSSIBLE SIMPLE) SCRIPT TO CAPTURE
COOMAND WINDOW OUTPUT
something like:
self.text_box = QtGui.QPlainTextEdit()
text= capured output
self.text_box.setPlainText(text)
def grid_layout (self) :
grid = QtGui.QGridLayout()
grid.setSpacing(2)
grid.addWidget(self.btn_newSearch , 1 , 1)
grid.addWidget(self.text_box , 2 , 1)
self.setLayout(grid)
def some_funtion (self) :
print "hello world"
def main():
app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Arrow key event handling in PyObjC

I am trying to make an app using PyObjC and am struggling to find how to record arrow key (left and right). I would like to be able to record every time the user presses the left and right arrow keys. I am using another example found online. Instead of the buttons used in previous example for increment and detriment, I would like to use the arrow keys on the key board. Been looking a while and thought I could get some help here. Thanks!
from Cocoa import *
from Foundation import NSObject
class TAC_UI_Controller(NSWindowController):
counterTextField = objc.IBOutlet()
def windowDidLoad(self):
NSWindowController.windowDidLoad(self)
# Start the counter
self.count = 0
#objc.IBAction
def increment_(self, sender):
self.count += 1
self.updateDisplay()
#objc.IBAction
def decrement_(self, sender):
self.count -= 1
self.updateDisplay()
def updateDisplay(self):
self.counterTextField.setStringValue_(self.count)
if __name__ == "__main__":
app = NSApplication.sharedApplication()
# Initiate the contrller with a XIB
viewController = test.alloc().initWithWindowNibName_("test")
# Show the window
viewController.showWindow_(viewController)
# Bring app to top
NSApp.activateIgnoringOtherApps_(True)
from PyObjCTools import AppHelper
AppHelper.runEventLoop()
Your NSView-derived class should implement a keyDown_ and / or keyUp_. You also need to have acceptsFirstResponder return True:
from AppKit import NSView
class MyView(NSView)
def keyDown_(self, event):
pass
def keyUp_(self, event):
pass
def acceptsFirstResponder(self):
return True
Here'a an example implementation from the PyObjC documentation you can use: https://pythonhosted.org/pyobjc/examples/Cocoa/AppKit/DragItemAround/index.html

PySide crash on exit (using QCompleter)

I have reproduced the "Custom Completer Example" from the Qt documentation using PySide
(Python 2.7.3, PySide 1.1.2, Qt 4.8.1).
I have an issue where a win32 exception is thrown on exit (or on Mac OS X a access violation exception).
On the Mac I can see a stack trace and the issue occurs during garbage collection, where references to QObjects are apparently not consistent, such that things go bad.
I can see this crash with the following self-contained script, only if a completer insertion was accepted. I.e. type the first few letters, then accept the completion.
On the other hand, if I have seen the completion list popup, but not accepted the completion, no crash occurs on exit.
################################################################################
# Completer.py
#
# A PySide port of the Qt 4.8 "Custom Completer Example"
# http://qt-project.org/doc/qt-4.8/tools-customcompleter.html
#
################################################################################
from PySide.QtCore import *
from PySide.QtGui import *
class TextEdit(QPlainTextEdit):
def __init__(self, parent=None):
super(TextEdit, self).__init__(parent)
self.c = None
def completer(self):
return self.c
def setCompleter(self, completer):
if self.c:
QObject.disconnect(self.c, 0, self, 0)
self.c = completer
if not self.c:
return
self.c.setWidget(self)
self.c.setCompletionMode(QCompleter.PopupCompletion)
self.c.setCaseSensitivity(Qt.CaseInsensitive)
self.c.activated.connect(self.insertCompletion)
def insertCompletion(self, completion):
if self.c.widget() is not self:
return
tc = self.textCursor()
extra = len(completion) - len(self.c.completionPrefix())
tc.movePosition(QTextCursor.Left)
tc.movePosition(QTextCursor.EndOfWord)
tc.insertText(completion[-extra:])
self.setTextCursor(tc)
def textUnderCursor(self):
tc = self.textCursor()
tc.select(QTextCursor.WordUnderCursor)
return tc.selectedText()
def focusInEvent(self, event):
if self.c:
self.c.setWidget(self)
super(TextEdit, self).focusInEvent(event)
def keyPressEvent(self, e):
if self.c and self.c.popup().isVisible():
if e.key() in (Qt.Key_Enter,
Qt.Key_Return,
Qt.Key_Escape,
Qt.Key_Tab,
Qt.Key_Backtab):
e.ignore()
return
# Check for the shortcut combination Ctrl+E
isShortcut = (e.modifiers() & Qt.ControlModifier) and e.key() == Qt.Key_E
# Do not process the shortcut when we have a completion
if not self.c or not isShortcut:
super(TextEdit, self).keyPressEvent(e)
noText = not e.text()
ctrlOrShift = e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)
if not self.c or (ctrlOrShift and noText):
return
eow = "~!##$%^&*()_+{}|:\"<>?,./;'[]\\-=" # End of word
hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift
completionPrefix = self.textUnderCursor()
if not isShortcut and \
(hasModifier or noText or len(completionPrefix) < 1 or e.text()[-1:] in eow):
self.c.popup().hide()
return
if completionPrefix != self.c.completionPrefix():
self.c.setCompletionPrefix(completionPrefix)
self.c.popup().setCurrentIndex( self.c.completionModel().index(0,0) )
cr = self.cursorRect()
cr.setWidth(self.c.popup().sizeHintForColumn(0) + \
self.c.popup().verticalScrollBar().sizeHint().width())
self.c.complete(cr)
class Completer(QMainWindow):
words = ("one",
"two",
"three",
"four")
def __init__(self, parent=None):
super(Completer, self).__init__(parent)
self.setWindowTitle("Completer")
self.textEdit = TextEdit()
self.completer = QCompleter(self)
self.completer.setModelSorting(QCompleter.CaseInsensitivelySortedModel)
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
self.completer.setWrapAround(False)
self.completer.setModel(QStringListModel(Completer.words, self.completer))
self.textEdit.setCompleter(self.completer)
self.setCentralWidget(self.textEdit)
self.resize(500, 300)
self.setWindowTitle("Completer")
if __name__ == '__main__':
import sys
from PySide.QtGui import QApplication
app = QApplication(sys.argv)
window = Completer()
window.show()
sys.exit(app.exec_())

Resources