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())
Related
I am trying to create something like this (autocomplete places) in pyqt5 QLineEdit.
There is a class called QCompleter with which i can suggest the content, but it requires an already formed list, but this google places api is a suggestion based function, how can i send each keystroke to google api and get the suggestion back and load in Qtextedit, is there a better way to do it
For this case you can create a custom model that makes a request using Place Autocomplete, and set that model to a QCompleter:
import json
from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork
API_KEY = "<API_KEY>"
class SuggestionPlaceModel(QtGui.QStandardItemModel):
finished = QtCore.pyqtSignal()
error = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(SuggestionPlaceModel, self).__init__(parent)
self._manager = QtNetwork.QNetworkAccessManager(self)
self._reply = None
#QtCore.pyqtSlot(str)
def search(self, text):
self.clear()
if self._reply is not None:
self._reply.abort()
if text:
r = self.create_request(text)
self._reply = self._manager.get(r)
self._reply.finished.connect(self.on_finished)
loop = QtCore.QEventLoop()
self.finished.connect(loop.quit)
loop.exec_()
def create_request(self, text):
url = QtCore.QUrl("https://maps.googleapis.com/maps/api/place/autocomplete/json")
query = QtCore.QUrlQuery()
query.addQueryItem("key", API_KEY)
query.addQueryItem("input", text)
query.addQueryItem("types", "geocode")
query.addQueryItem("language", "en")
url.setQuery(query)
request = QtNetwork.QNetworkRequest(url)
return request
#QtCore.pyqtSlot()
def on_finished(self):
reply = self.sender()
if reply.error() == QtNetwork.QNetworkReply.NoError:
data = json.loads(reply.readAll().data())
if data['status'] == 'OK':
for prediction in data['predictions']:
self.appendRow(QtGui.QStandardItem(prediction['description']))
self.error.emit(data['status'])
self.finished.emit()
reply.deleteLater()
self._reply = None
class Completer(QtWidgets.QCompleter):
def splitPath(self, path):
self.model().search(path)
return super(Completer, self).splitPath(path)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self._model = SuggestionPlaceModel(self)
completer = Completer(self, caseSensitivity=QtCore.Qt.CaseInsensitive)
completer.setModel(self._model)
lineedit = QtWidgets.QLineEdit()
lineedit.setCompleter(completer)
label = QtWidgets.QLabel()
self._model.error.connect(label.setText)
lay = QtWidgets.QFormLayout(self)
lay.addRow("Location: ", lineedit)
lay.addRow("Error: ", label)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.resize(400, w.sizeHint().height())
w.show()
sys.exit(app.exec_())
I faced the same issue, at first I tried #eyllanasec's answer. But for some reason, the performance is not so good (often stuck or hang) in the platform that I use (I was creating a plugin for Qt-based software using PyQt). It is running fine independently though, so perhaps it's specific to my platform.
In the end, I found a Qt example project for a similar case, Google Suggest. You can find it https://doc.qt.io/qt-5/qtnetwork-googlesuggest-example.html. It creates a custom class to handle the search and the pop-up window (using QTreeWidget).
Since the example is in Qt, I port it to PyQt with small modification https://github.com/ismailsunni/scripts/blob/master/autocomplete_from_url.py
I wonder if it is possible to change the languages(translations) dynamically without using qt designer to make the UI? That means I don't want to use the function retranslateUi() to update the program interface.
Here is my code, but I'm stuck on lines marked #1 #2 #3. Don't know what I should use to update the interface.
import sys
from PyQt5.QtCore import Qt, QTranslator
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel,
QComboBox, QVBoxLayout
class Demo(QWidget):
def __init__(self):
super(Demo, self).__init__()
self.button = QPushButton(self.tr('Start'), self)
self.label = QLabel(self.tr('Hello, World'), self)
self.label.setAlignment(Qt.AlignCenter)
self.combo = QComboBox(self)
self.combo.addItem('English')
self.combo.addItem('中文')
self.combo.addItem('français')
self.combo.currentTextChanged.connect(self.change_func)
self.trans = QTranslator(self)
self.v_layout = QVBoxLayout()
self.v_layout.addWidget(self.combo)
self.v_layout.addWidget(self.button)
self.v_layout.addWidget(self.label)
self.setLayout(self.v_layout)
def change_func(self):
print(self.combo.currentText())
if self.combo.currentText() == '中文':
self.trans.load('eng-chs')
_app = QApplication.instance()
_app.installTranslator(self.trans)
# 1
elif self.combo.currentText() == 'français':
self.trans.load('eng-fr')
_app = QApplication.instance()
_app.installTranslator(self.trans)
# 2
else:
_app = QApplication.instance()
_app.removeTranslator(self.trans)
# 3
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
Any help would be appreciated.
TL; DR; It is not necessary to use Qt Designer
You should not use Qt Designer necessarily but you should use the same technique, that is, create a method that could be called retranslateUi() and in it set the texts using translate() instead of tr() (for more details read the docs). Calling that method when you change language for it must use the changeEvent() event. For example in your case the code is as follows:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Demo(QtWidgets.QWidget):
def __init__(self):
super(Demo, self).__init__()
self.button = QtWidgets.QPushButton()
self.label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
self.combo = QtWidgets.QComboBox(self)
self.combo.currentIndexChanged.connect(self.change_func)
self.trans = QtCore.QTranslator(self)
self.v_layout = QtWidgets.QVBoxLayout(self)
self.v_layout.addWidget(self.combo)
self.v_layout.addWidget(self.button)
self.v_layout.addWidget(self.label)
options = ([('English', ''), ('français', 'eng-fr' ), ('中文', 'eng-chs'), ])
for i, (text, lang) in enumerate(options):
self.combo.addItem(text)
self.combo.setItemData(i, lang)
self.retranslateUi()
#QtCore.pyqtSlot(int)
def change_func(self, index):
data = self.combo.itemData(index)
if data:
self.trans.load(data)
QtWidgets.QApplication.instance().installTranslator(self.trans)
else:
QtWidgets.QApplication.instance().removeTranslator(self.trans)
def changeEvent(self, event):
if event.type() == QtCore.QEvent.LanguageChange:
self.retranslateUi()
super(Demo, self).changeEvent(event)
def retranslateUi(self):
self.button.setText(QtWidgets.QApplication.translate('Demo', 'Start'))
self.label.setText(QtWidgets.QApplication.translate('Demo', 'Hello, World'))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
Then generate the .ts:
pylupdate5 main.py -ts eng-chs.ts
pylupdate5 main.py -ts eng-fr.ts
Then use Qt Linguist to do the translations.
And finally the .qm:
lrelease eng-fr.ts eng-chs.qm
The complete project you find here.
No you will have to use a technique like Qt Designer does with retranslateUi because the Qt widget system does not have a way to redo the translation on it's own (otherwise QT Designer would be using that to).
Building such a system would require a fundamental change to the widgets as for each string property you would need to know that it contains a translatable string (not a data value) and knowing the original string for looking up the new translation would be best (reversing translations could be ambiguous).
Sorry. I will modify the contents. I would like to load a widget inside def test by pressing Qbutton. Can not you use QStackedWidget to load the widget's configured functions? I've compiled the class and called it, but only a = QLineEdit ('Qline', self). I wonder what should be done to switch widgets.
You can also create a table like html using pyqt5.
import sys
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.stacked = QStackedWidget(self)
self.FirstpUI()
def FirstpUI(self):
self.btn1 = QPushButton('test1', self)
self.btn1.move(50,50)
self.btn1.clicked.connect(self.btn1_click)
def test(self):
a = QLineEdit('Qline', self)
b = QLineEdit('Qline2', self)
c = QPushButton('button', self)
a.move(0, 0)
b.move(100, 0)
c.move(50,50)
c.clicked.connect(self.btn2_click)
def btn1_click(self):
self.btn1.deleteLater()
self.stacked.addWidget(self.test())
self.stacked.setCurrentIndex(self.stacked.currentIndex()+1)
def btn2_click(self):
QMessageBox.about(self,'hello','hello2')
if __name__ == "__main__":
app = QApplication(sys.argv)
fream = MainWindow()
fream.show()
app.exec_()
May be I don't know what you real want,because I know that little, I think You can use QtDesigner,it's very useful
I have two lines using the old SIGNAL and SLOT style..
combobox.emit(SIGNAL("activated(int)"), combobox.currentIndex())
combobox.emit(SIGNAL("activated(const QString &)"), combobox.currentText())
I was wondering what the new style would look like. I'm new to python and I don't have much experience with signals and slots. Is there a really good resource floating around that covers this? The documentation didn't really help me understand what was going on.
The solution is to indicate the type of argument of the signal that is being emitted:
combo.activated[type].connect(someSlot)
Example:
class Widget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setLayout(QVBoxLayout())
combo = QComboBox(self)
self.layout().addWidget(combo)
combo.addItems(["item1", "item2", "item3"])
combo.activated[int].connect(self.onActivatedIndex)
combo.activated[str].connect(self.onActivatedText)
#pyqtSlot(int)
def onActivatedIndex(self, index):
print(index)
#pyqtSlot(str)
def onActivatedText(self, text):
print(text)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Consider a simple example like this which links two sliders using signals and slots:
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class MyMainWindow(QWidget):
def __init__(self):
QWidget.__init__(self, None)
vbox = QVBoxLayout()
sone = QSlider(Qt.Horizontal)
vbox.addWidget(sone)
stwo = QSlider(Qt.Horizontal)
vbox.addWidget(stwo)
sone.valueChanged.connect(stwo.setValue)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyMainWindow()
w.show()
sys.exit(app.exec_())
How would you change this so that the second slider moves in the opposite direction as the first? Slider one would be initialized with these values:
sone.setRange(0,99)
sone.setValue(0)
And slider two would be initialized with these values:
stwo.setRange(0,99)
stwo.setValue(99)
And then the value of stwo would be 99 - sone.sliderPosition.
How would you implement the signal and slot to make this work? I would appreciate a working example that builds on the simple example above.
Your example is a bit broken, because you forgot to set the parent of the layout, and also to save the slider widgets as member attributes to be accessed later... But to answer your question, its really as simple as just pointing your connection to your own function:
class MyMainWindow(QWidget):
def __init__(self):
QWidget.__init__(self, None)
vbox = QVBoxLayout(self)
self.sone = QSlider(Qt.Horizontal)
self.sone.setRange(0,99)
self.sone.setValue(0)
vbox.addWidget(self.sone)
self.stwo = QSlider(Qt.Horizontal)
self.stwo.setRange(0,99)
self.stwo.setValue(99)
vbox.addWidget(self.stwo)
self.sone.valueChanged.connect(self.sliderChanged)
def sliderChanged(self, val):
self.stwo.setValue(self.stwo.maximum() - val)
Note how sliderChanged() has the same signature as the original setValue() slot. Instead of connecting one widget directly to the other, you connect it to a custom method and then transform the value to what you want, and act how you want (setting a custom value on stwo)
You can connect signals to functions that do things. Your code isn't structured to do that easily and required refactoring, so you can do it the easy way:
stwo.setInvertedAppearance(True)
sone.valueChanged.connect(stwo.setValue)
Here's the way I did it. I added this class which reimplements setValue. (I got the idea from http://zetcode.com/tutorials/pyqt4/eventsandsignals/)
class MySlider(QSlider):
def __init__(self):
QSlider.__init__(self, Qt.Horizontal)
def setValue(self, int):
QSlider.setValue(self, 99-int)
Here's the complete code. Is this a good approach?
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class MySlider(QSlider):
def __init__(self):
QSlider.__init__(self, Qt.Horizontal)
def setValue(self, int):
QSlider.setValue(self, 99-int)
class MyMainWindow(QWidget):
def __init__(self):
QWidget.__init__(self, None)
vbox = QVBoxLayout()
sone = QSlider(Qt.Horizontal)
sone.setRange(0,99)
sone.setValue(0)
vbox.addWidget(sone)
stwo = MySlider()
stwo.setRange(0,99)
stwo.setValue(0)
vbox.addWidget(stwo)
sone.valueChanged.connect(stwo.setValue)
self.setLayout(vbox)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyMainWindow()
w.show()
sys.exit(app.exec_())