My code looks like this
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setGeometry(400, 400, 600, 480)
self.setWindowTitle("HBCheat")
self.initUI()
def initUI(self):
self.label = QtWidgets.QLabel(self)
self.label.setText("Reaction Cheat")
self.label.move(10, 0)
self.b1 = QtWidgets.QPushButton(self)
self.b1.setText("Toggle: off")
self.b1.move(10, 35)
self.b1.clicked.connect(self.reaction_cheat)
def click(self,x,y):
win32api.SetCursorPos((x,y))
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0)
time.sleep(0.01)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0)
def reaction_cheat(self):
self.b1.setText("Toggle: on")
while not keyboard.is_pressed('q'):
try:
if pyautogui.pixel(1298, 415)[1] == 219:
self.click(1298, 415)
except:
continue
self.b1.setText("Toggle: off")
Whenever I click on button "b1" it should run the function reaction_cheat. Which it does. But the problem is that the text for the button does not change and the window crashes. If I press the button it still works as intended but the button text does not change. And if I were to comment out the "while not keyboard.is_pressed('q'):" loop. It would change the buttons text and the function would end.
So Why is it that the the text for b1 doesn't change and the window crashes.
Also when I press q it doesn't stop the window from crashes. But it still stops the loop and stops the function from running.
The problem is caused because the logic that checks if the key q is pressed or does not consume a lot of time blocking the eventloop.
A possible solution is to use threads but another solution is to use the add_hotkey function that allows to use a callback avoiding the use of while loops. You should also avoid using time.sleep(). In this case the logic is that if the button is pressed then the task of verifying the pixel will be executed every T seconds until the user presses the q key.
from functools import cached_property
from PyQt5.QtCore import pyqtSignal, QObject, QTimer
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton
import pyautogui
import keyboard
import win32api
import win32con
class KeyBoardHelper(QObject):
pressed = pyqtSignal()
def __init__(self, hotkey="", parent=None):
super().__init__(parent)
self._hotkey = hotkey
#property
def hotkey(self):
return self._hotkey
#hotkey.setter
def hotkey(self, hotkey):
self.stop()
self._hotkey = hotkey
def start(self):
keyboard.add_hotkey(self.hotkey, self._callback)
def stop(self):
try:
keyboard.remove_hotkey(self._callback)
except KeyError:
pass
def _callback(self):
self.pressed.emit()
class MouseHelper:
def click(self, x, y):
win32api.SetCursorPos((x, y))
self._press()
def _press(self):
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0)
QTimer.singleShot(10, self._release)
def _release(self):
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setGeometry(400, 400, 600, 480)
self.setWindowTitle("HBCheat")
self.initUI()
self.keyboard_helper.pressed.connect(self.handle_keyboard_pressed)
self.keyboard_helper.hotkey = "q"
self.timer.timeout.connect(self.verify_pixel)
#cached_property
def keyboard_helper(self):
return KeyBoardHelper()
#cached_property
def mouse_helper(self):
return MouseHelper()
#cached_property
def timer(self):
return QTimer(interval=10)
def initUI(self):
self.label = QLabel(self)
self.label.setText("Reaction Cheat")
self.label.move(10, 0)
self.b1 = QPushButton(self)
self.b1.setText("Toggle: off")
self.b1.move(10, 35)
self.b1.clicked.connect(self.reaction_cheat)
def click(self, x, y):
print("X")
""""
"""
def reaction_cheat(self):
self.b1.setText("Toggle: on")
self.keyboard_helper.start()
self.timer.start()
def handle_keyboard_pressed(self):
self.keyboard_helper.stop()
self.timer.start()
self.b1.setText("Toggle: off")
def verify_pixel(self):
try:
if pyautogui.pixel(1298, 415)[1] == 219:
self.mouse_helper.click(1298, 415)
except:
pass
def main():
import sys
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Related
I have an application where I run some process in a second thread. At some points during this process, given a certain condition is met, another dialog window opens, which halts the process until you confirm something. This causes the following error message:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QApplication(0x1f9c82383d0), parent's thread is QThread(0x1f9c7ade2a0), current thread is QThread(0x1f9c8358800)
Interestingly, if you also move your cursor over the MainWindow while the process is running, and before the new dialog pops up, it also produces this error message a couple of times:
QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
Very strange. Because it only occurs if you move your cursor over the MainWindow.
Now, in my application, I actually load an interface for the new dialog that pops up using PyQt5.uic.loadUi, and this hasn't caused any problems. However, when I was creating the example for this post, another issue occurred, due to the fact that I was setting the layout of the new dialog during its initialization:
QObject::setParent: Cannot set parent, new parent is in a different thread
Which results in the application crashing:
Process finished with exit code -1073741819 (0xC0000005)
I'm obviously doing something wrong here regarding the threading I would guess, but I don't know what. I am especially baffled by the fact that I cannot set the layout of the new dialog during its initialization, while using loadUi is totally fine. Here is my example code:
import sys
import time
import numpy as np
from PyQt5.QtCore import QObject, pyqtSignal, QThread
from PyQt5.QtWidgets import (
QDialog, QApplication, QPushButton, QGridLayout, QProgressBar, QLabel
)
class SpecialDialog(QDialog):
def __init__(self):
super().__init__()
btn = QPushButton('pass variable')
btn.clicked.connect(self.accept)
layout = QGridLayout()
layout.addWidget(btn)
# self.setLayout(layout)
self.variable = np.random.randint(0, 100)
class Handler(QObject):
progress = pyqtSignal(int)
finished = pyqtSignal(int)
def __init__(self):
super().__init__()
self._isRunning = True
self._success = False
def run(self):
result = None
i = 0
while i < 100 and self._isRunning:
if i == np.random.randint(0, 100):
dialog = SpecialDialog()
dialog.exec_()
result = dialog.variable
time.sleep(0.01)
i += 1
self.progress.emit(i)
if i == 100:
self._success = True
self.finished.emit(result)
def stop(self):
self._isRunning = False
class MainWindow(QDialog):
def __init__(self):
super().__init__()
btn = QPushButton('test')
btn.clicked.connect(self.run_test)
self.pbar = QProgressBar()
self.resultLabel = QLabel('Result:')
layout = QGridLayout(self)
layout.addWidget(btn)
layout.addWidget(self.pbar)
layout.addWidget(self.resultLabel)
self.setLayout(layout)
self.handler = None
self.handler_thread = QThread()
self.result = None
def run_test(self):
self.handler = Handler()
self.handler.moveToThread(self.handler_thread)
self.handler.progress.connect(self.progress)
self.handler.finished.connect(self.finisher)
self.handler_thread.started.connect(self.handler.run)
self.handler_thread.start()
def progress(self, val):
self.pbar.setValue(val)
def finisher(self, result):
self.result = result
self.resultLabel.setText(f'Result: {result}')
self.pbar.setValue(0)
self.handler.stop()
self.handler.progress.disconnect(self.progress)
self.handler.finished.disconnect(self.finisher)
self.handler_thread.started.disconnect(self.handler.run)
self.handler_thread.terminate()
self.handler = None
if __name__ == '__main__':
app = QApplication(sys.argv)
GUI = MainWindow()
GUI.show()
sys.exit(app.exec_())
EDIT
I forgot to mention that I already found this post, which may be related to my problem, however, I don't undestand the reasoning of the solution in the top answer, and more importantly, I don't speak what I believe is C++.
If we analyze the case before equality happens it is the same case after that equality happens unless the initial progress is different, with this it indicates that when equality happens a signal must be sent to the GUI to ask for the information, and when you have that information, launch the same task but with an initial progress equal to what you had before the signal was emitted.
from functools import partial
import sys
import time
import numpy as np
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, QThread, QTimer
from PyQt5.QtWidgets import (
QDialog,
QApplication,
QPushButton,
QGridLayout,
QProgressBar,
QLabel,
)
class SpecialDialog(QDialog):
def __init__(self):
super().__init__()
btn = QPushButton("pass variable")
btn.clicked.connect(self.accept)
layout = QGridLayout()
layout.addWidget(btn)
# self.setLayout(layout)
self.variable = np.random.randint(0, 100)
class Handler(QObject):
progress = pyqtSignal(int)
finished = pyqtSignal()
resultChanged = pyqtSignal(int)
requestSignal = pyqtSignal()
def __init__(self):
super().__init__()
self._isRunning = True
self._success = False
self.iter = 0
self.result = None
#pyqtSlot()
def start(self):
self._isRunning = True
self._success = False
self.iter = 0
self.result = None
self.task()
#pyqtSlot()
#pyqtSlot(int)
def task(self, value=None):
if value is not None:
self.result = value
while self.iter < 100 and self._isRunning:
if self.iter == np.random.randint(0, 100):
self.requestSignal.emit()
return
time.sleep(0.01)
self.iter += 1
self.progress.emit(self.iter)
if self.iter == 100:
self._success = True
if self.result:
self.resultChanged.emit(self.result)
self.finished.emit()
def stop(self):
self._isRunning = False
class MainWindow(QDialog):
def __init__(self):
super().__init__()
btn = QPushButton("test")
btn.clicked.connect(self.run_test)
self.pbar = QProgressBar()
self.resultLabel = QLabel("Result:")
layout = QGridLayout(self)
layout.addWidget(btn)
layout.addWidget(self.pbar)
layout.addWidget(self.resultLabel)
self.setLayout(layout)
self.handler = None
self.handler_thread = QThread()
self.result = None
def run_test(self):
self.handler = Handler()
self.handler.moveToThread(self.handler_thread)
self.handler.progress.connect(self.progress)
self.handler.resultChanged.connect(self.on_result_changed)
self.handler.finished.connect(self.on_finished)
self.handler.requestSignal.connect(self.onRequestSignal)
self.handler_thread.started.connect(self.handler.start)
self.handler_thread.start()
def progress(self, val):
self.pbar.setValue(val)
def onRequestSignal(self):
dialog = SpecialDialog()
dialog.exec_()
wrapper = partial(self.handler.task, dialog.variable)
QTimer.singleShot(0, wrapper)
#pyqtSlot(int)
def on_result_changed(self, result):
self.result = result
self.resultLabel.setText(f"Result: {result}")
#pyqtSlot()
def on_finished(self):
self.pbar.setValue(0)
self.handler.stop()
self.handler_thread.quit()
self.handler_thread.wait()
if __name__ == "__main__":
app = QApplication(sys.argv)
GUI = MainWindow()
GUI.show()
sys.exit(app.exec_())
I'm setting up a new desktop widget to make my life easier at work and using QPropertyAnimation to make it pretty. Fading the app in and out doesn't seem to want to work and in typical coder fashion, it's brought my progress to a standstill.
I'm implementing QPropertyAnimation in a personalised class to make my life easier, but since it's not intially worked I've taken it back to the class code and it's still being pretty stubborn. So far I've tried.
class widget(QWidget):
def init(self):
self.setSize(QSize(300, 300))
self.setWindowOpacity(1)
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.setAttribute(Qt.WA_TranslucentBackground)
def paintEvent(self, event):
s = self.size()
qp = QPainter()
qp.begin(self)
qp.setRenderHint(QPainter.Antialiasing, True)
qp.setBrush(QColor().fromRgb(2,106,194))
qp.setPen(QColor().fromRgb(2,106,194))
qp.drawRoundRect(QRect(0,0, 300, 300), 16, 8)
qp.end()
def show(self):
self.superShow()
a = QPropertyAnimation(self, "windowOpacity")
a.setDuration(500)
a.setStartValue(1)
a.setEndValue(0)
a.start()
def hide(self):
a = QPropertyAnimation(self, "windowOpacity")
a.setDuration(500)
a.setStartValue(0)
a.setEndValue(1)
a.finished.connect(self.superHide)
a.start()
def superShow(self):
super(widget, self).show()
def superHide(self):
super(widget, self).hide()
No error messages at all it just hides and shows after the animation duration is over. No idea where to look or what to do to get it working. I've only been coding for like 3 months or so.
Your code has many errors, for example:
I don't see where you call init().
Animations are local variables that will be removed when the show and hide methods are finished, which is almost instantaneous.
etc.
Instead of changing the opacity directly I will use QGraphicsOpacityEffect, instead of using the show and close method, I will use the showEvent, hideEvent and closeEvent methods.
import sys
from PySide2.QtCore import QEasingCurve, QEventLoop, QPropertyAnimation, QRect, QSize, Qt
from PySide2.QtGui import QColor, QPainter
from PySide2.QtWidgets import QAction, QApplication, QGraphicsOpacityEffect, QWidget
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(QSize(300, 300))
# self.setWindowOpacity(1)
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setContextMenuPolicy(Qt.ActionsContextMenu)
quit_action = QAction(self.tr("E&xit"), self)
quit_action.setShortcut(self.tr("Ctrl+Q"))
quit_action.triggered.connect(self.close)
self.addAction(quit_action)
effect = QGraphicsOpacityEffect(self, opacity=1.0)
self.setGraphicsEffect(effect)
self._animation = QPropertyAnimation(
self,
propertyName=b"opacity",
targetObject=effect,
duration=500,
startValue=0.0,
endValue=1.0,
)
def paintEvent(self, event):
qp = QPainter(self)
qp.setRenderHint(QPainter.Antialiasing, True)
qp.setBrush(QColor().fromRgb(2, 106, 194))
qp.setPen(QColor().fromRgb(2, 106, 194))
qp.drawRoundedRect(QRect(0, 0, 300, 300), 16, 8)
def fade_in(self):
self._animation.setDirection(QPropertyAnimation.Forward)
self._animation.start()
def fade_out(self):
loop = QEventLoop()
self._animation.finished.connect(loop.quit)
self._animation.setDirection(QPropertyAnimation.Backward)
self._animation.start()
loop.exec_()
def showEvent(self, event):
super().showEvent(event)
self.fade_in()
def closeEvent(self, event):
# fade out
self.fade_out()
super().closeEvent(event)
def hideEvent(self, event):
# fade out
self.fade_out()
super().hideEvent(event)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I’m using PyQt5 and Python 3.6. I want to use the ENTER (or RETURN) key for dual purposes.
If the user enters text into the combo box and then hits the ENTER key, then I want the text from the combo box to be appended to a list. In all other situations, I want the ENTER key to serve as a shortcut for a push button.
I can’t find the correct decision for how the handle when ENTER is pressed. Here is a code sample. I’m looking to a decision in the returnDecision(self) function (toward the bottom of the script).
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication, QShortcut
from PyQt5.QtWidgets import QComboBox
from PyQt5.QtGui import QKeySequence
from PyQt5.QtCore import Qt, QSize
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 200)
btn = QPushButton('Button', self)
btn.move(100, 50)
btn.clicked.connect(self.btnPrint)
self.comboBox = QComboBox(self)
self.comboBox.setEditable(True)
self.comboBox.move(100, 150)
self.comboBox.setMinimumSize(QSize(150, 0))
self.comboBox.setEditText("Initial Text")
self.comboBox.editTextChanged.connect(self.cboxPrint)
enter = QShortcut(QKeySequence(Qt.Key_Return), self)
enter.activated.connect(self.returnDecision)
self.textList = []
self.show()
def btnPrint(self):
print("Button was pressed")
def btnAction(self):
print("RETURN pressed when NOT editing combo box")
self.btnPrint()
def cboxPrint(self):
print(self.comboBox.currentText())
def cboxAction(self):
print("RETURN pressed when editing combo box")
self.textList.append(self.comboBox.currentText())
print(self.textList)
def returnDecision(self):
if ENTER KEY WAS PRESSED WHILE EDITING COMBO BOX:
self.cboxAction()
else:
self.btnAction()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Any suggestions?
One way to solve this is to use a custom subclass of the QComboBox and override the keyPressEvent method. Then also implement a keyPressEvent in your widget and handle each differently.
class CustomCombo(QtWidgets.QComboBox):
enter_pressed = QtCore.pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Return:
self.enter_pressed.emit()
else:
QtWidgets.QComboBox.keyPressEvent(self, event)
# if the key is not return, handle normally
class Example(QWidget):
def __init__(self):
# Code here
self.combo_box = CustomCombo(self)
self.combo_box.enter_pressed.connect(self.cboxAction)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Return:
self.btnAction()
raiseTemmie.py
import random
import sys
import time
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import QWidget, QLabel, QApplication
class Example(QWidget):
size=100
imgNum=0
imgQuantity=2
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowFlags(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setStyleSheet("background-color:transparent;")
self.setGeometry(100, 100, 100, 100)
self.setWindowFlags(Qt.SplashScreen | Qt.WindowStaysOnTopHint)
self.label=QLabel(self)
self.pixmaps=[QPixmap('left.png'),QPixmap('right.png'),QPixmap('stand.png')]
for x in range(len(self.pixmaps)):
self.pixmaps[x]=self.pixmaps[x].scaled(self.size,self.size,Qt.KeepAspectRatio)
self.label.setPixmap(self.pixmaps[2])
self.resize(self.pixmaps[2].width(),self.pixmaps[2].height())
self.show()
def moving(self):
distance=random.randint(10,40)
direct =[random.randint(-1,2),random.randint(-1,2)]
for x in range(0,distance):
self.changeFoot()
self.move(self.x()+5*direct[0],self.y()+5*direct[1])
time.sleep(0.05)
# self.changeTimer.stop()
def changeFoot(self):
if self.imgNum<self.imgQuantity-1:
self.imgNum+=1
else :
self.imgNum=0
self.label.setPixmap(self.pixmaps[self.imgNum])
def mousePressEvent(self, QMouseEvent):
self.label.setPixmap(self.pixmaps[2])
self.changeTimer.stop()
changeTimer=QTimer()
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_Escape:
sys.exit()
if QKeyEvent.key() == Qt.Key_G:
self.moving()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
I thought this code would switch the pixmap and move it simultaneously but it do not work well.
The timer start after moving finished. What is the problem?
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_Escape:
sys.exit()
if QKeyEvent.key() == Qt.Key_G:
self.moving()
if I press 'G', it starts the changeTimer and calls moving()
A possible solution is to use qApp.processEvents() to update the data, this is recommended when tasks require little time as is your case, since the maximum time of the loop is 0.05sec*40=2sec.
def moving(self):
distance=random.randint(10,40)
direct =[random.randint(-1,2),random.randint(-1,2)]
for x in range(0,distance):
self.changeFoot()
self.move(self.x()+5*direct[0],self.y()+5*direct[1])
time.sleep(0.05)
qApp.processEvents()
The solution may fail if time grows, another solution is to implement the timer and separate the tasks correctly as I show below:
class Example(QWidget):
size=100
imgNum=0
imgQuantity=2
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowFlags(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setStyleSheet("background-color:transparent;")
self.setGeometry(100, 100, 100, 100)
self.setWindowFlags(Qt.SplashScreen | Qt.WindowStaysOnTopHint)
self.label=QLabel(self)
self.pixmaps=[QPixmap('left.png'),QPixmap('right.png'),QPixmap('stand.png')]
for x in range(len(self.pixmaps)):
self.pixmaps[x]=self.pixmaps[x].scaled(self.size,self.size,Qt.KeepAspectRatio)
self.label.setPixmap(self.pixmaps[2])
self.resize(self.pixmaps[2].width(),self.pixmaps[2].height())
self.changeTimer=QTimer(self)
self.changeTimer.timeout.connect(self.onTimeout)
self.show()
def moving(self):
self.distance= random.randint(10,40)
self.direct = QPoint(random.randint(-1,2), random.randint(-1,2))
self.changeTimer.start(50)
def onTimeout(self):
if self.distance == 0:
self.changeTimer.stop()
else:
self.changeFoot()
self.move(self.pos() + self.direct)
self.distance -= 1
def changeFoot(self):
if self.imgNum<self.imgQuantity-1:
self.imgNum+=1
else :
self.imgNum=0
self.label.setPixmap(self.pixmaps[self.imgNum])
def mousePressEvent(self, QMouseEvent):
self.label.setPixmap(self.pixmaps[2])
self.changeTimer.stop()
def keyPressEvent(self, QKeyEvent):
if QKeyEvent.key() == Qt.Key_Escape:
self.close()
if QKeyEvent.key() == Qt.Key_G:
self.moving()
Note: It is advisable to use self.close() instead of sys.exit() since the first gives time for the program to free resources and close properly.
Hi I want to add the QProgressBar behind the QLIneEdit, just like it is in Safari Browser or IE, So here is my starting point how can I hook the ProgressBar and MyLineEdit together so that when user is done entering the path the progress bar should show the progress while the path is opened !!!
from PyQt4 import QtGui, QtCore
import sys
class ProgressBar(QtGui.QProgressBar):
""" docstring for ProgressBar
"""
def __init__(self, parent=None):
super(ProgressBar, self).__init__(parent)
self.timer = QtCore.QBasicTimer()
self.step = 0
self.doAction()
def timerEvent(self, e):
if self.step >= 100:
self.timer.stop()
return
self.step = self.step + 15
self.setValue(self.step)
def doAction(self):
if self.timer.isActive():
self.timer.stop()
else:
self.timer.start(100, self)
class MyLineEdit(QtGui.QLineEdit):
""" docstring for MyLineEdit
"""
def __init__(self, parent=None):
super(MyLineEdit, self).__init__(parent)
# I want to hook this bar at the backgroind of MyLineEdit
pbar = ProgressBar()
class Example(QtGui.QWidget):
def __init__(self, parent=None):
super(Example, self).__init__(parent)
self.pbar = ProgressBar(self)
self.editbx = MyLineEdit(self.pbar)
newPalette = QtGui.QPalette()
newPalette.setColor(self.editbx.backgroundRole(), QtCore.Qt.transparent)
self.editbx.setPalette(newPalette)
self.editbx.setText("Defaukt text set")
self.editbx.setStyleSheet("QLineEdit { border:none;}")
self.pbar.setStyleSheet("QProgressBar {border:none;}")
self.initUI()
def initUI(self):
# self.pbar.setGeometry(30, 40, 200, 25)
self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('QtGui.QProgressBar')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
win = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I also looking forward to add a QCombobox in place of text entered so it can list the other existing folders, not the way QCompleter uses though becuase it doesnt has look of QCombobox, and I do not want to allow user to enter anything that doesnt exists.
Any help would be greatly appreciated.
I've attached an example of a QLineEdit with a progress bar behind it. It was heavily influenced by this post: http://www.qtcentre.org/threads/54758-Progress-bar-form-QLineEdit-issue
Basically you have to manage painting yourself. Unfortunately it didn't seem to work when I tried to do the same thing with a QComboBox. I would suggest posting a new question specifically about painting a progress bar on a QComboBox once you get up to it!
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MyLineEdit(QLineEdit):
def __init__(self, parent=None):
QLineEdit.__init__(self, parent)
self.timer = QBasicTimer()
self.step = 0
self.doAction()
def timerEvent(self, e):
if self.step >= 100:
self.timer.stop()
return
self.step = self.step + 10
self.repaint()
def doAction(self):
if self.timer.isActive():
self.timer.stop()
else:
self.timer.start(1000, self)
def generateGradient(self, color):
gradient = QLinearGradient(0, 0, 0, self.height());
m_defaultBaseColor = self.palette().color(QPalette.Base)
gradient.setColorAt(0, m_defaultBaseColor)
gradient.setColorAt(0.15, color.lighter(120))
gradient.setColorAt(0.5, color)
gradient.setColorAt(0.85, color.lighter(120))
gradient.setColorAt(1, m_defaultBaseColor)
return gradient
def paintEvent(self, event):
p = QPainter(self)
panel = QStyleOptionFrameV2()
self.initStyleOption(panel)
self.style().drawPrimitive(QStyle.PE_PanelLineEdit, panel, p, self)
# an alternative to painting the QLineEdit is to do it only when the widget has focus and the progress bar is finished
#if self.hasFocus() or self.step >= 100: QLineEdit.paintEvent(self, event)
# however I've chosen to paint it always
QLineEdit.paintEvent(self, event)
painter = QPainter(self)
lenap = QStyleOptionFrameV2()
self.initStyleOption(lenap)
backgroundRect = self.style().subElementRect(QStyle.SE_LineEditContents, lenap, self)
# some alternative if statements you might like to use instead...
#
# if not self.hasFocus() and self.step < 100:
# if self.step < 100:
if True:
loadingColor = QColor(116,192,250)
painter.setBrush(self.generateGradient(loadingColor))
painter.setPen(Qt.transparent)
mid = int(backgroundRect.width()/100.0*self.step)
progressRect = QRect(backgroundRect.x(), backgroundRect.y(), mid, backgroundRect.height())
painter.drawRect(progressRect)
painter.setPen(Qt.SolidLine)
painter.drawText(backgroundRect, Qt.AlignLeft|Qt.AlignVCenter, " " + self.text())
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self._control = QWidget()
self.setCentralWidget(self._control)
l = QVBoxLayout(self._control)
e = MyLineEdit()
l.addWidget(e)
b = QPushButton('a')
l.addWidget(b)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Window()
sys.exit(app.exec_())