Why is this basic PyQt5 program not working? - python

This is a pyqt5 program that is suppossed to set the content of one lineedit to the content of the other lineedit. It is not working in that it you can't edit the line edits. Can someone tell me why and how I can fix it?
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
firstenter = QLineEdit(self)
firstenter.move(140,276)
firstenter.setFixedWidth(240)
secondenter = QLineEdit(self)
secondenter.move(420,276)
secondenter.setFixedWidth(240)
equalsighn = QLabel(self)
equalsighn.setText("=")
equalsighn.move(395,270)
def conversion():
try:
a = firstenter.displayText()
b = secondenter.displayText()
if firstenter.isModified() == True:
firstenter.setText(str(b))
elif secondenter.isModified() == True:
secondenter.setText(str(a))
except:
if firstenter.isModified() == True:
secondenter.setText("")
if secondenter.isModified() == True:
firstenter.setText("")
firstenter.textEdited.connect(conversion)
secondenter.textEdited.connect(conversion)
self.setGeometry(500, 300, 800, 500)
self.setWindowTitle('Test')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle('Fusion')
ex = Example()
sys.exit(app.exec_())

It's not working because the logic in the first if statement is wrong:
if firstenter.isModified() == True:
firstenter.setText(str(b))
You're resetting the contents of the first line edit to the second, but since the secondenter text is empty to begin with, it sets the firstenter text accordingly, thus resetting its contents no matter what you type. Because of that, the second if won't work for the same reason.
The solution is much simpler, and uses [sender()] to know which object emitted the signal:
def conversion():
if self.sender() == firstenter:
secondenter.setText(firstenter.text())
else:
firstenter.setText(secondenter.text())
Note that you should use text() and not displayText(), since the latter might return a different result if the echoMode() is not set to Normal (the default).
Also, the try/exception is useless, and you shouldn't check against isModified, since textEdited is always and only emitted when the user changes the text, and not when the text is changed programmatically using setText().
A suggestion: avoid fixed sizes and positions, using them will probably result in an unusable interface on systems different from yours (because of various reasons, including screen size, DPI and default fonts). You should prefer layout managers instead.

Related

Complex context-menu submenu

I have a Qt5 application mainly driven by context menu.
Right now I have the standard structure with menu(s), submenu(s) and actions.
I would like to add, in place of a submenu, a small dialog with a few input widgets, something like this:
Is there any (possibly simple) way to get this?
I know I can open a normal dialog from popup, but that is not what I mean.
I would like to have normal submenu behavior, with chance to go back to parent menu... if possible.
Note: I'm actually using PyQt5, but I think this is a more general Qt question.
Following #G.M. advice I was able to partially solve my problem.
My code current code looks like:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class ActionFont(QWidgetAction):
def __init__(self, parent: QWidget, target: QPlainTextEdit):
super(ActionFont, self).__init__(parent)
self.setIcon(QIcon("font-face.svg"))
self.setText("Face")
w = QFontComboBox()
w.currentFontChanged.connect(self.doit)
self.setDefaultWidget(w)
self.cursor = target.textCursor()
self.char_format = self.cursor.charFormat()
font = self.char_format.font()
w.setCurrentFont(font)
# self.triggered.connect(self.doit)
def doit(self, font):
self.char_format.setFont(font)
self.cursor.setCharFormat(self.char_format)
class ActionSize(QWidgetAction):
def __init__(self, parent: QWidget, target: QPlainTextEdit):
super(ActionSize, self).__init__(parent)
self.setIcon(QIcon("font-size.svg"))
self.setText("Size")
self.has_changed = False
w = QSpinBox()
self.setDefaultWidget(w)
self.cursor = target.textCursor()
self.char_format = self.cursor.charFormat()
font = self.char_format.font()
size = font.pointSize()
w.setRange(6, 100)
w.setValue(size)
w.valueChanged.connect(self.doit)
w.editingFinished.connect(self.quit)
def doit(self, size):
print(f'ActionSize.doit({size})')
self.char_format.setFontPointSize(size)
self.cursor.setCharFormat(self.char_format)
self.has_changed = True
def quit(self):
print(f'ActionSize.quit()')
if self.has_changed:
print(f'ActionSize.quit(quitting)')
class Window(QMainWindow):
def __init__(self, parent=None):
from lorem import text
super().__init__(parent)
self.text = QPlainTextEdit(self)
self.setCentralWidget(self.text)
self.text.setContextMenuPolicy(Qt.CustomContextMenu)
self.text.customContextMenuRequested.connect(self.context)
self.text.appendPlainText(text())
self.setGeometry(100, 100, 1030, 800)
self.setWindowTitle("Writer")
def context(self, pos):
m = QMenu(self)
w = QComboBox()
w.addItems(list('ABCDE'))
wa = QWidgetAction(self)
wa.setDefaultWidget(w)
m.addAction('Some action')
m.addAction(wa)
m.addAction('Some other action')
sub = QMenu(m)
sub.setTitle('Font')
sub.addAction(ActionFont(self, self.text))
sub.addAction(ActionSize(self, self.text))
m.addMenu(sub)
pos = self.mapToGlobal(pos)
m.move(pos)
m.show()
app = QApplication([])
w = Window()
w.show()
app.exec()
This works with a few limitations:
I have been able to add just a single widget using setDefaultWidget(), if I try to
add a fill QWidget or a container (e.g.: QFrame) nothing appears in menu.
I have therefore not been able to prepend an icon (or a QLabel) to the widget.
Widget does not behave like a menu item (it does not close when activated); I tried to overcome that as implemented in ActionSize, but looks rather kludgy and I'm unsure if it's the right way to go.
I will therefore not accept my own answer in hope someone can refine it enough to be generally useful.

Displaying Gif Loading in PyQt4

I am trying to display a loading gif while other code executes. I am very unfamiliar with PyQt and have tried following the code at this link, which seems to be the standard way of executing a gif. I only want the gif playing and do not want a button. Here is the code I am currently at, but it is very poor.
self.movie = QMovie(coffeeloading.gif, self)
size = self.movie.scaledSize()
self.movie_screen.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.movie_screen.setAlignment(Qt.AlignCenter)
self.movie_screen = QLabel()
main_layout = QVBoxLayout()
main_layout.addWidget(self.movie_screen)
self.setLayout(main_layout)
self.movie.setCacheMode(QMovie.CacheAll)
self.movie.setSpeed(100)
self.movie_screen.setMovie(self.movie)
self.movie.start()
This is supposed to execute after a button press and fill up the whole screen of 240x320, but I dont have any idea how to do it. I have already read through many of the other stackoverflow and other links, but none of them seem to address how to complete this.
As an alternative, you can use this class here.
QtWaitingSpinner extends the QtWidgets.QWidget class and you can start your spinner by simply running
your_QtWaitingSpinnerObject.start()
You can check this answer as well.
# -*- coding: utf-8 -*-
import sys
from PySide.QtGui import *
from PySide.QtCore import *
class test_widget(QWidget):
m_play_state = False
def __init__(self):
super(test_widget, self).__init__()
self.__ui__()
def __ui__(self):
t_lay_parent = QVBoxLayout()
self.m_label_gif = QLabel()
self.m_button_play = QPushButton("Play")
t_lay_parent.addWidget(self.m_label_gif)
t_lay_parent.addWidget(self.m_button_play)
self.m_movie_gif = QMovie("loding.gif")
self.m_label_gif.setMovie(self.m_movie_gif)
self.m_label_gif.setScaledContents(True)
self.m_label_gif.hide()
self.setLayout(t_lay_parent)
self.m_button_play.clicked.connect(self.slt_play)
def slt_play(self):
if self.m_play_state:
self.m_label_gif.hide()
self.m_movie_gif.stop()
self.m_play_state = False
else:
self.m_label_gif.show()
self.m_movie_gif.start()
self.m_play_state = True
if __name__ == "__main__":
app = QApplication(sys.argv)
win = test_widget()
win.show()
sys.exit(app.exec_())

How to get a value of QTimeEdit,QCheckBox and QDateTimeEdit into a variable. (Python)

I'm starting with PyQt4 and I'm making a program for practice. In that program I have QDateTimeEdit, QTimeEdit and QCheckBox.
How would I pull values from them into a string using signals and slots.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class main_frame(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.initUI()
def initUI(self):
# A push buttnon
btn_get = QPushButton("Get Time", self)
btn_get.move(100, 250)
#
time = QTime()
# Find what is the local/system time
curent_t = time.currentTime()
# Convert it to a str
# The output shoud be HH:MM:SS , eg 10:45:28
curent_t_str = curent_t.toString()
# Create a timeEdit widget
time_widget = QTimeEdit(time, self)
time_widget.setTime(curent_t)
def get_time():
print(curent_t_str)
btn_get.clicked.connect(get_time)
### At the end of the day you got curent_t_str variable
### which you can use it in your code down the road
### so I belive that`s helpful for your needs
### implement this may vary, depending your needs ...
# Set a costom size for the mainWindow
self.setFixedSize(300, 300)
def main():
app = QApplication(sys.argv)
myapp = main_frame()
myapp.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Does that help you out ? Note that for QDateTimeEdit the procedure is the same, after you got your value into format like 10:45:28 is up to you how you gonna format the string or for what you gonna use

Uppercase input in QLineEdit python way

I had drawn up an UI using the QT Designer but found out that there are no parameters for me to set QLineEdit inputs to be uppercase.
After doing some online searching, I have only seen a very few handful of results that cater to my needs, however all are coded in Qt. Example, this link
And so, are there ways for me to do this in the pythonic way?
The simplest way would be to use a validator.
This will immediately uppercase anything the user types, or pastes, into the line-edit:
from PyQt4 import QtCore, QtGui
class Validator(QtGui.QValidator):
def validate(self, string, pos):
return QtGui.QValidator.Acceptable, string.upper(), pos
# for old code still using QString, use this instead
# string.replace(0, string.count(), string.toUpper())
# return QtGui.QValidator.Acceptable, pos
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.edit = QtGui.QLineEdit(self)
self.validator = Validator(self)
self.edit.setValidator(self.validator)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.edit)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 100)
window.show()
sys.exit(app.exec_())
Try this,
I believe this serves your purpose. I won't call it much pythonic. More like PyQt override.
#minor code edit
from PyQt4 import QtGui
import sys
#===============================================================================
# MyEditableTextBox-
#===============================================================================
class MyEditableTextBox(QtGui.QLineEdit):
#|-----------------------------------------------------------------------------|
# Constructor
#|-----------------------------------------------------------------------------|
def __init__(self,*args):
#*args to set parent
QtGui.QLineEdit.__init__(self,*args)
#|-----------------------------------------------------------------------------|
# focusOutEvent :-
#|-----------------------------------------------------------------------------|
def focusOutEvent(self, *args, **kwargs):
text = self.text()
self.setText(text.__str__().upper())
return QtGui.QLineEdit.focusOutEvent(self, *args, **kwargs)
#|--------------------------End of focusOutEvent--------------------------------|
#|-----------------------------------------------------------------------------|
# keyPressEvent
#|-----------------------------------------------------------------------------|
def keyPressEvent(self, event):
if not self.hasSelectedText():
pretext = self.text()
self.setText(pretext.__str__().upper())
return QtGui.QLineEdit.keyPressEvent(self, event)
#|--------------------End of keyPressEvent-------------------------------------|
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
lay = QtGui.QHBoxLayout()
w.setLayout(lay)
le1 = MyEditableTextBox()
lay.addWidget(le1)
le2 = MyEditableTextBox()
lay.addWidget(le2)
w.show()
sys.exit(app.exec_())
Hey I know I am kind of late, but I hope this might help some one else like me who spent some time searching for this
Mycase:
I was trying to convert only the first letter to capital and this is what I ended up with and it worked (just a beginner in python so if you can make this more pythonic please let me know)
In the defining function: line_edit_object.textChanged.connect(lambda:auto_capital(line_edit_object))
the function auto_capital:
def auto_capital(line_edit_object):
edit=line_edit_object
text=edit.text()
edit.setText(text.title())
this shall fix every issue. Feel free to make it more pythonic.
I am also late but after contemplating on this question I think this is some sort of pythonic way of accomplishing it in PyQt5:
class CustomInput(QLineEdit):
def __init__(self):
super().__init__()
self.textChanged.connect(self.text_changed)
def text_changed(self):
if self.text().isupper():
return
self.setText(self.text().upper())

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')

Categories