PyQt5 / PySide2 code only executes works with ascii characters - python

I have made a script extension for a programm called Nuke which opens a dialog with lineedit and allows the user to enter a label. However the script only executes setLabel() by pressing enter when there are only ascii characters in the lineedit.
text() returns unicode and Nuke has no problem with special characters like äöü in labels if you do it through the normal ui
Here is my code:
# -*- coding: utf-8 -*-
from PySide2 import QtCore, QtGui, QtWidgets
import nuke
import sys
import os
class setLabelTool(QtWidgets.QDialog):
def __init__(self, node):
self.n = node
super(setLabelTool, self).__init__()
self.setObjectName("Dialog")
self.setWindowTitle("Test")
self.setFixedSize(199, 43)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
self.lineEdit = QtWidgets.QLineEdit(self)
self.lineEdit.setGeometry(QtCore.QRect(10, 10, 181, 25))
self.lineEdit.setObjectName("lineEdit")
self.lineEdit.setFocusPolicy(QtCore.Qt.StrongFocus)
self.lineEdit.setAlignment(QtCore.Qt.AlignHCenter)
currentlabel = self.n['label'].value()
if len(currentlabel) == 0:
self.lineEdit.setPlaceholderText("Set node label")
else:
self.lineEdit.setText(currentlabel)
self.lineEdit.selectAll()
self.lineEdit.returnPressed.connect(self.setLabel)
def setLabel(self):
label = self.lineEdit.text()
self.n['label'].setValue(label)
print ("Node: " + self.n['name'].value() + " labeled " + label)
self.close()
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
print "Exit setLabelTool"
self.close()
def showLabelTool():
n = nuke.selectedNodes()[-1]
if n != None:
Tool = setLabelTool(n)
Tool.exec_()
else:
print "Error in showLabelTool()"

I had the same problem yesterday.
text() returns of type unicode and your method needs a string object, to convert just use encode('utf-8')

This might be a bit out of context, but there's easier ways to change the label for a node in Nuke using getInput if that's what you're trying to accomplish:
new_label = nuke.getInput("enter label")
nuke.selectedNode()['label'].setValue(new_label)

Related

Barcode scanning with PyQt5

I have a usb barcode scanner which I am connecting to my computer. Everytime it scans a barcode, it types the data into the computer like a keyboard. My goal was to have the data be typed into PyQT5 Table widget.
I have created the table below and I simply scan the items into it. The problem is that when I scan an item, it edits the first cell, but the cursor does not move automatically to the next row so I can scan a new item into the table. I have to click on the second cell and then scan the item. Then click on the third cell and scan the item and so on.
I was wondering how I can automate it so that after an item is scanned into the first cell, it automatically moves to the next cell and waits for input from the scanner?
import sys
from PyQt5.QtWidgets import *
#Main Window
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'Specimen Dashboard'
self.setWindowTitle(self.title)
self.tableWidget = QTableWidget()
self.createTable()
self.tableWidget.itemChanged.connect(self.go_to_next_row)
self.layout = QVBoxLayout()
self.layout.addWidget(self.tableWidget)
self.setLayout(self.layout)
self.show()
def go_to_next_row(self):
#Not working
#Trying to see if I can automatically move to next cell, but editing it
self.tableWidget.setItem(1,0, QTableWidgetItem("Name"))
#Create table
def createTable(self):
self.tableWidget.setRowCount(4)
self.tableWidget.setColumnCount(2)
self.tableWidget.horizontalHeader().setStretchLastSection(True)
self.tableWidget.horizontalHeader().setSectionResizeMode(
QHeaderView.Stretch)
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
By default the scanners send an endline("\n") that is translated a Return or Enter key and this by default closes the editor, in this case that event must be intercepted, move the cursor and open the editor:
import sys
from PyQt5 import QtCore, QtWidgets
class TableWidget(QtWidgets.QTableWidget):
def keyPressEvent(self, event):
if (
event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return)
and self.state() == QtWidgets.QAbstractItemView.EditingState
):
index = self.moveCursor(
QtWidgets.QAbstractItemView.MoveNext, QtCore.Qt.NoModifier
)
self.selectionModel().setCurrentIndex(
index, QtCore.QItemSelectionModel.ClearAndSelect
)
self.edit(index)
else:
super().keyPressEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.tableWidget = TableWidget(4, 2)
self.setCentralWidget(self.tableWidget)
self.tableWidget.horizontalHeader().setStretchLastSection(True)
self.tableWidget.horizontalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch
)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
You can subclass the table and overwrite closeEditor(): the hint argument tells the view what should happen when the editor has been closed; by default, when pressing Enter the current cell data is submitted, but you can override this behavior like this:
from PyQt5 import QtGui, QtWidgets
class Table(QtWidgets.QTableView):
# leave to False for the default behavior (the next cell is the one at the
# right of the current, or the first of the next row; when set to True it
# will always go to the next row, while keeping the same column
useNextRow = False
def closeEditor(self, editor, hint):
if hint == QtWidgets.QAbstractItemDelegate.SubmitModelCache:
if self.useNextRow:
super().closeEditor(editor, hint)
current = self.currentIndex()
newIndex = current.sibling(current.row() + 1, current.column())
if newIndex.isValid():
self.setCurrentIndex(newIndex)
self.edit(newIndex)
return
else:
hint = QtWidgets.QAbstractItemDelegate.EditNextItem
super().closeEditor(editor, hint)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
test = Table()
test.show()
model = QtGui.QStandardItemModel(10, 5)
test.setModel(model)
sys.exit(app.exec_())

How do I direct console output to a pyqt5 plainTextEdit widget with Python?

I am trying to display console output of a python script in a QplainTextEdit widget in PyQt5.
I am getting this error:
TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
I have defined my objects in the pyqt GUI file and I believe that I have all the imports.
Update
I have amended the code in this question:
from PyQt5.QtCore import QRectF, Qt
from PyQt5.QtWidgets import QFileDialog, QPlainTextEdit
from PyQt5 import QtCore, QtGui, QtWidgets
from PIL import Image, ImageQt, ImageEnhance
# from PyQt5.QtGui import Qt
from pyqtgraph.examples.text import text
from covid19gui_V3 import Ui_MainWindow
import os
import sys
input_img = Image.open("/home/ironmantis7x/Documents/Maverick_AI/Python/keras-covid-19/maverickAI30k.png")
text_edit = QPlainTextEdit()
class EmittingStream(QtCore.QObject):
textWritten = QtCore.pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
textWritten = QtCore.pyqtSignal(str)
def __init__(self, parent=None, **kwargs):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.ShowIButton.clicked.connect(self.do_test)
self.chooseStudy.clicked.connect(self.do_choosestudy)
self.RunButton_3.clicked.connect(self.do_runstudy)
self.scene = QtWidgets.QGraphicsScene(self)
self.graphicsView.setScene(self.scene)
w, h = input_img.size
self.pixmap_item = self.scene.addPixmap(QtGui.QPixmap())
# self.graphicsView.fitInView(QRectF(0, 0, w, h), Qt.KeepAspectRatio)
self.graphicsView.update()
self.plainTextEdit.update()
self.level = 1
self.enhancer = None
self.timer = QtCore.QTimer(interval=500, timeout=self.on_timeout)
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
def write(self, text):
self.textWritten.emit(str(text))
#QtCore.pyqtSlot()
def do_test(self):
# input_img = Image.open("/home/ironmantis7x/Documents/Maverick_AI/Python/keras-covid-19/maverickAI30k.png")
self.enhancer = ImageEnhance.Brightness(input_img)
self.timer.start()
self.ShowIButton.setDisabled(True)
#QtCore.pyqtSlot()
def on_timeout(self):
if self.enhancer is not None:
result_img = self.enhancer.enhance(self.level)
qimage = ImageQt.ImageQt(result_img)
self.pixmap_item.setPixmap(QtGui.QPixmap.fromImage(qimage))
if self.level > 7:
self.timer.stop()
self.enhancer = None
self.level = 0
self.ShowIButton.setDisabled(False)
self.level = 1
self.ShowIButton.setDisabled(False)
#QtCore.pyqtSlot()
def do_choosestudy(self):
dlg = QFileDialog()
dlg.setFileMode(QFileDialog.AnyFile)
if dlg.exec_():
filenames = dlg.selectedFiles()
f = open(filenames[0], 'r')
#QtCore.pyqtSlot()
def do_runstudy(self):
os.system("df -h")
# filetext = open('screenout.txt').read()
# filetext.close()
# textViewValue = self.plainTextEdit.toPlainText()
# QPlainTextEdit.appendPlainText(self, str(textViewValue))
# sys.stdout = self(textWritten=self.textWritten)
self.normalOutputWritten(text_edit)
def __del__(self):
# Restore sys.stdout
sys.stdout = sys.__stdout__
def normalOutputWritten(self, text_edit):
#cursor = self.plainTextEdit.textCursor()
#cursor.movePosition(QtGui.QTextCursor.End)
#cursor.insertText(text_edit)
self.plainTextEdit.appendPlainText(text_edit)
#self.plainTextEdit.ensureCursorVisible()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
How can I make this work correctly?
Update 2
I indeed DID do research into the topic and this is one of the main resources I used to try to solve the issue before I posted my question: How to capture output of Python's interpreter and show in a Text widget?
Update 3
I have revised my code in the post to reflect code suggestions in the link I used to help me with my issue.
I am still unable to get this to run correctly. I now get this error:
self.plainTextEdit.appendPlainText(text_edit) TypeError:
appendPlainText(self, str): argument 1 has unexpected type
'QPlainTextEdit'
I have a user interface, TableManagerWindow, that I've been maintaining and developing in Qt designer. After converting via pyuic to a *.py file, I was able to implement what Ferdinand Beyer had suggested in the link you provided above. Simple button to print text to terminal and it indeed does get appended to the QTextEdit widget via append(). Not sure this fits the bill for you for some reason, but I can vouch that it worked for me as well. I'm not savvy enough to get the nuance that is causing your issue, but figured I'd put this here just in case. Admins feel free to delete this if it's extraneous, but it works.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
# Define a stream, custom class, that reports data written to it, with a Qt signal
class EmittingStream(QtCore.QObject):
textWritten = QtCore.pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
class Ui_TableManagerWindow(object):
def setupUi(self, TableManagerWindow):
#define all of my widgets, layout, etc here
.
.
.
# Install a custom output stream by connecting sys.stdout to instance of EmmittingStream.
sys.stdout = EmittingStream(textWritten=self.output_terminal_written)
# Create my signal/connections for custom method
self.source_dir_button.clicked.connect(self.sourceDirButtonClicked)
self.retranslateUi(TableManagerWindow)
QtCore.QMetaObject.connectSlotsByName(TableManagerWindow)
def retranslateUi(self, TableManagerWindow):
.
.
.
#custom method that prints to output terminal. The point is to have this emmitted out to my QTextEdit widget.
def sourceDirButtonClicked(self):
for i in range(10):
print("The Source DIR button has been clicked " + str(i) + " times")
#custom method to write anything printed out to console/terminal to my QTextEdit widget via append function.
def output_terminal_written(self, text):
self.output_terminal_textEdit.append(text)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
TableManagerWindow = QtWidgets.QMainWindow()
ui = Ui_TableManagerWindow()
ui.setupUi(TableManagerWindow)
TableManagerWindow.show()
sys.exit(app.exec_())

Python - Gtk - paste from clipboard

While building a simple GUI video downloader in python and with Glade3 I am stuck with pasting into a text entry box using an icon.
The application, and paste icon.
The python code:
# !/usr/python
import sys
import os
import subprocess as sp
try:
from gi.repository import Gtk, Gdk
except:
print('Gtk not available')
sys.exit(1)
try:
import pyGtk
pyGtk.require('2.0')
except:
pass
class VideoDownloader:
def on_mainwindow_destroy(self, object, data=None):
print "quit with cancel"
Gtk.main_quit()
def __init__(self):
self.gladefile = "lib/videodownloder.glade"
self.builder = Gtk.Builder()
self.builder.add_from_file(self.gladefile)
self.builder.connect_signals(self)
self.go = self.builder.get_object
self.window = self.go("mainwindow")
self.window.show()
self.okbtn = self.go("okbutton")
self.cancelbtn = self.go("cancelbutton")
#self.restartswitch = self.go("restartswitch")
self.contswitch = self.go("contswitch")
self.vlcswitch = self.go("vlcswitch")
self.urlentry = self.go("urlentry")
self.filechooser = self.go("filechooser")
self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
def on_urlentry_activate(self, widget):
url = self.urlentry.get_text()
print("the url is:" + url)
def on_urlentry_icon_press(self):
text = self.clipboard.set_text(self.urlentry.get_text(), -1)
print("the urlentry paste icon was clicked | 'text' ")
def on_urlentry_icon_press(self):
text = self.clipboard.wait_for_text()
print("the urlentry paste icon was clicked | 'text' ")
if text != None:
self.urlentry.set_text(text)
print(" 'text' has been pasted to the urlentry")
else:
print("No text on the clipboard")
def on_filechooser_file_activated(self, widget):
myfile = self.filechooser.get_uri()
print("the file is: " + myfile)
def on_vlcswitch_activate(self, widget):
print("VLC Switch has been activated")
def on_contswitch_activate(self, widget):
print("Continue switch has been acivated")
def on_quitbutton_clicked(self, button):
print("quit with the close button")
Gtk.main_quit()
def on_okbutton_clicked(self, button):
myfile = self.filechooser.get_uri()
url = self.urlentry.get_text()
wgetcmd = ("wget -O 'myfile ' 'url' ")
print("ok button has been clicked")
print("wget will now be run with your args: " +myfile+url)
os.system(wgetcmd)
if __name__ == "__main__":
print("videodownloader is running")
notify = os.system("notify-send --expire-time=5000 --icon='img/vid-down-logo.png' --app-name=VideoDownloader 'VideoDownloader' 'The VideoDownloader app is now running and ready!'")
notify
main = VideoDownloader()
Gtk.main()
print("videodownloader has stopped running")
When I run the code it mostly works but when I click the paste icon I get an error:
TypeError: on_urlentry_icon_press() takes exactly 1 argument (4 given)
I am fairly new to python and glade so I am probably making an elementary mistake but I do not know what is wrong and what the error means. I have searched but found only advice that didn't help.
Such as this..
Any suggestions on how to fix this please?
Your current on_urlentry_icon_press method only takes the self argument but it should take more than one because it is connected to the icon-press signal.
In the documentation you can see that it takes:
entry (a reference to the Gtk.Entry)
position (whether it is the primary or secondary icon)
event (e.g. whether it was a double click)
So it should look like this:
def on_urlentry_icon_press(self, entry, position, event):
print('urlentry icon clicked')

Class-calling function not working

I am trying to modify this piece of code by calling out a import window then follow by this code.
As I am also going to use the current piece of code (which is not written by me), the way it works is that when user selects one of the 3 prefixes ['a','b','c'], it will change the naming of the items in Maya accordingly.
Part of the former Coding (prefix window):
import maya.cmds as cmds
import maya.mel as mel
import pymel.core as pm
from PyQt4 import QtGui, QtCore
import sys, os
class createUI(QtGui.QFrame):
def __init__(self, parent=None):
QtGui.QFrame.__init__(self, parent)
self.shot = SHOT
self.initUI()
self.connections()
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.resize(400,100)
self.move(300,300)
self.setWindowTitle('Select the prefix of rexPass')
self.pubDock = createUI()
vLayout = QtGui.QVBoxLayout()
vLayout.addWidget(self.pubDock)
self.setLayout(vLayout)
self.createConnection()
def createConnection(self):
self.connect( self.pubDock.cancelButton, QtCore.SIGNAL( "clicked()" ), self.close )
self.connect( self.pubDock.OKButton, QtCore.SIGNAL( "clicked()" ), self.close )
def setupRenderGlobals():
cmds.setAttr ('StartRange.multiFrame', 1)
cmds.setAttr ('EndRange.endFrame', 200)
cmds.setAttr ('WidthRes.xres', 1920)
cmds.setAttr ('HeightRes.yres', 1080)
def main():
setupRenderGlobals()
global app
app=QtGui.qApp
global form
form = MainWindow()
form.show()
Currently I would like to add on a function where it calls a selection window to import something, and once the selection is done, it will then calls out the above code.
The problem I have is where when user hits the import button in the import window, it automatically closes, and the perfix window is not showing up, or I would have the 2 windows showing up or just the prefix window and not the import window
My Coding:
class Processing():
'In-house code to call out the import window and they will have the name of 'prItems_a01''
importItems = procureItems.api.importItem()
allItems = pm.ls(type="prItems")
if allItem < 2 :
test = MainWindow()
else:
print ('testing')
Any advices?
The problem is here:
if allItem < 2 :
test = MainWindow()
else:
print ('testing')
allItems = pm.ls(type="prItems")
if allItem < 2 :
test = MainWindow()
pymel.core.ls returns a list, while 2 is an int. Python may not do what you expect here. From the docs:
Objects of different types except numbers are ordered by their type names; objects of the same types that don’t support proper comparison are ordered by their address.
So, "list" > "int"
What you probably meant to do is check the len of allItem, like this:
def processing():
# ~~ your code ~~ #
if len(allItem) < 2:
test = MainWindow()
else:
print ('testing')

Detecting Key Sequences

I am using pyqt's Qshortcut in trying to detect a key combination to trigger some action i.e when
the user types -> into a QtextEdit widget, I would like to print "changing mode".
I have seen other key sequence examples that involve CTRL+E or some other CTRL or shift key
combination,
self.shcut1 = QtGui.QShortcut(self)
self.shcut1.setKey("CTRL+E")
self.connect(self.shcut1, QtCore.SIGNAL("activated()"), self.close)
But I really want to trap ->(hyphen followed by a greater than sign). Any suggestions on how to do this
QShortCut only accepts combinations of QtCore.Qt::KeyboardModifiers. Try using an event filter:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from PyQt4 import QtGui, QtCore
class MyWindow(QtGui.QTextEdit):
modeChanged = QtCore.pyqtSignal(bool)
_seenMinus = False
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.installEventFilter(self)
self.modeChanged.connect(self.on_modeChanged)
def on_modeChanged(self):
print "Changing Mode."
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.KeyPress:
if event.key() == QtCore.Qt.Key_Minus:
self._seenMinus = True
elif event.key() == QtCore.Qt.Key_Greater \
and event.modifiers() == QtCore.Qt.ShiftModifier \
and self._seenMinus:
self.modeChanged.emit(True)
self.setStyleSheet("""
background-color: lightgray;
""")
elif event.modifiers() != QtCore.Qt.ShiftModifier:
if self._seenMinus == True:
self.modeChanged.emit(False)
self._seenMinus = False
self.setStyleSheet("")
return super(MyWindow, self).eventFilter(obj, event)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.show()
sys.exit(app.exec_())
Just catch the signal QTextEdit::textChanged(), and every the user makes a change, scan the text for '->'. Granter the brute force approach of scanning the entire text block every time isn't pretty; another option is scanning only the last two characters of the text. However this misses the case where the user creates '->' by deleting text in between a '-' and a '>' character. If you're not worried about that case, then just go with the last two. QTextEdit::cursorPositionChanged might allow you to test precisely at the insertion/deletion point.

Categories