wxPython: Detecting mouse click on a bitmap - onclick

I'm trying to detect a clic on a bitmap with wxpython.
The on_click procedure seems to run without clic, and not to run when clicking.
What do I do wrong?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
import os
import wx
import wx.lib.agw.thumbnailctrl as TC
import Image
filename="Raphael-Poli-angle.png"
class MyForm(wx.Frame):
def on_clic(self):
print "clicked"
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Choose Dot in Picture", size=(700,500))
self.panel = wx.Panel(self, wx.ID_ANY)
png = wx.Image(filename, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
image=wx.StaticBitmap(self.panel, -1, png, (1, 1), (png.GetWidth(), png.GetHeight()))
image.Bind(wx.EVT_LEFT_DOWN, self.on_clic())
self.Show(True)
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()

You do not have to "input an event". The event comes from outside - depending on your action.
Change the "def on_clic..." to
def on_clic(self, evt):
and also change "image.Bind..." to
image.Bind(wx.EVT_LEFT_DOWN, self.on_clic)
And if you change your on_clic to:
def on_clic(self, evt):
x, y=evt.GetPosition()
print "clicked at", x, y
...you can also get the position, where the image was clicked.
See: https://wxpython.org/docs/api/wx.MouseEvent-class.html

all event handlers must except the event as an argument. on_clic should be
def on_clic(self, event):
print "clicked"

Related

PyQT QListWidgetItem specific item launch specific function

Using the QTDesigner I made a simple QListWidge that contains three items
Apple
Banana
Orange
My Goal, when one of those options is selected it launches a specific function. For some reason I cannot figure this out and I can't find anything within my Google searches. I am new to pyQT so maybe I'm just using the incorrect terms.
Using QT Designer I can set the SIGNAL and SLOT, but the effect is for every single item within the QListWidget, it is not specific.
Here is the code I am concerned with
QtCore.QObject.connect(self.listWidget, QtCore.SIGNAL(_fromUtf8("itemClicked(QListWidgetItem*)")), MainWindow.close)
That code is closing the main window when any QListWidgetItem is selected. I want it to only close when "apple" is selected. I want Banana and Orange to do something else.
Seems like all the examples I find online are not addressing if you want item A to do something vs item B. They all just use a generic sample in which all items do the same thing.
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'untitled.ui'
#
# Created by: PyQt4 UI code generator 4.11.4
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGuii
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(800, 600)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
self.listWidget = QtGui.QListWidget(self.centralwidget)
self.listWidget.setGeometry(QtCore.QRect(50, 60, 256, 241))
self.listWidget.setObjectName(_fromUtf8("listWidget"))
item = QtGui.QListWidgetItem()
self.listWidget.addItem(item)
item = QtGui.QListWidgetItem()
self.listWidget.addItem(item)
item = QtGui.QListWidgetItem()
self.listWidget.addItem(item)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName(_fromUtf8("menubar"))
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(MainWindow)
self.statusbar.setObjectName(_fromUtf8("statusbar"))
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QObject.connect(self.listWidget, QtCore.SIGNAL(_fromUtf8("itemClicked(QListWidgetItem*)")), MainWindow.close)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
__sortingEnabled = self.listWidget.isSortingEnabled()
self.listWidget.setSortingEnabled(False)
item = self.listWidget.item(0)
item.setText(_translate("MainWindow", "apple", None))
item = self.listWidget.item(1)
item.setText(_translate("MainWindow", "banana", None))
item = self.listWidget.item(2)
item.setText(_translate("MainWindow", "orange", None))
self.listWidget.setSortingEnabled(__sortingEnabled)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Was able to piece together the solution using the website below and some example scripts floating around. They key to the solution was the .item
.item(0) is reviewing what is currently set at index 0. The website below showed that I could attach a .isSelected() that would return True when a list item is selected.
With both pieces of information I can now take a specific action when a specific item is selected.
http://doc.qt.io/qt-4.8/qlistwidgetitem.html#isSelected
QtCore.QObject.connect(self.listWidget, QtCore.SIGNAL(_fromUtf8("itemClicked(QListWidgetItem*)")), self.closewindow)
def closewindow(self):
apple = self.listWidget.item(0).isSelected()
banana = self.listWidget.item(1).isSelected()
if apple == True:
self.exitprogram()
elif banana == True:
print 'Banana selected'
def exitprogram(self):
sys.exit()

Ending a QDrag Prematurely

I want my application to terminate all drag and drops in a dragLeaveEvent, without the user releasing the mouse button.
The problem is that the loop suspends all events that could cancel a QDrag while it is happening, even though the documentation states:
"On Linux and Mac OS X, the drag and drop operation can take some
time, but this function does not block the event loop. Other events
are still delivered to the application while the operation is
performed. On Windows, the Qt event loop is blocked during the
operation. However, QDrag.exec() on Windows causes processEvents() to
be called frequently to keep the GUI responsive. If any loops or
operations are called while a drag operation is active, it will block
the drag operation."
Because of this, I cannot call events which would end the drag.
So far, I've tried what is suggested here, as seen in the code. I'm using PyQt5, but if a solution works in Qt it should work in PyQt.
Edit: I'm a little scared to delete the drag, as the scene does not own it. I suppose I could set it up to own it though, but as was posted here it should not work.
Edit2: Added code with my non-working attempts to fix it. I'd really like to solve this issue without having to make my own drag-drop framework. Also trimmed post.
import sys
from PyQt5.QtWidgets import (QMainWindow, QApplication,
QGraphicsView, QGraphicsScene, QGraphicsWidget, QGraphicsRectItem)
from PyQt5.QtCore import (QMimeData, Qt, QByteArray, QCoreApplication,
QEvent, QPoint)
from PyQt5.QtGui import QBrush, QColor, QDrag, QPen, QMouseEvent
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.scene = CustomScene()
self.view = QGraphicsView(self.scene, self)
self.setGeometry(100, 100, 600, 600)
self.view.setGeometry(0, 0, 500, 500)
self.show()
class CustomScene(QGraphicsScene):
def __init__(self):
super().__init__()
self.customWidgets = []
for i in range(5):
newItem = CustomDragWidget()
self.addItem(newItem)
self.customWidgets.append(newItem)
newItem.setGeometry(i * 50, i * 50, 50, 50)
def dragLeaveEvent(self, event):
# Work your magic here. I've tried the following:
# 1)
self.customWidgets[0].dropEvent(event)
# 2)
self.dropEvent(event)
# 3)
eve = QMouseEvent(QEvent.MouseButtonRelease, QPoint(0, 0), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
QCoreApplication.sendEvent(self.views()[0], eve)
QCoreApplication.processEvents()
# 4)
eve = QMouseEvent(QEvent.MouseButtonRelease, QPoint(0, 0), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
QCoreApplication.sendEvent(self.customWidgets[0], eve)
QCoreApplication.processEvents()
def dropEvent(self, QGraphicsSceneDragDropEvent):
# a dummy dropevent that tries to stop the drop, but doesnt work
QGraphicsSceneDragDropEvent.accept()
class CustomDragWidget(QGraphicsWidget):
def __init__(self,):
super().__init__()
self.squareItem = QGraphicsRectItem()
self.squareItem.setBrush(QBrush(QColor(Qt.blue)))
self.squareItem.setPen(QPen(QColor(Qt.black), 2))
self.squareItem.setRect(0, 0, 50, 50)
self.squareItem.setParentItem(self)
self.setAcceptDrops(True)
def mousePressEvent(self, event):
mime = QMimeData()
itemData = QByteArray()
mime.setData('application/x-dnditemdata', itemData)
drag = QDrag(self)
drag.setMimeData(mime)
drag.exec(Qt.MoveAction)
def dropEvent(self, event):
event.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
This is a little bit hackish, but it seems to work (on Linux, anyway):
def dragLeaveEvent(self, event):
QCoreApplication.postEvent(self,
QKeyEvent(QEvent.KeyPress, Qt.Key_Escape, Qt.NoModifier))

Capturing Button Methods from a second Form using PyQT

I am super new to PyQT ... I am using QT Designer for my forms. I am confused about where to put my event handling for the second form in my project.
I have the following .py files :
main.py
Ui_make_render_folders.py (generated from the Qt designer ui file)
Ui_prefs.py (generated from the Qt designer ui file)
Mainwindow.py (this is where all the even handling for the main form
lives)
icons_rc.py (resource file)
make_render_folders.py (where all my custom functions live)
Here is a picture of the script running :
click to see script running << CLICK HERE
My question is ... where does the even handling for my prefs window go ???? I cant figure out where to put my [b]on_btn_released() for the buttons on y 2nd form.[/b] Also I am not sure I am instantiating that dialog properly. Currently I am adding this code to the top of my MainWindow.py.
Finally what does #pyqSignature("") do ? And do I need it ?
"""
This is where all teh MainWindow logic sits
"""
import os
import os.path
from PyQt4.QtGui import QMainWindow, QFileDialog, QTreeWidgetItem, QDialog
from PyQt4.QtCore import pyqtSignature, QString
from make_folders import make_render_folders
from Ui_make_render_folders import Ui_MainWindow
from Ui_prefs import Ui_prefs
class Prefs(QDialog, Ui_prefs):
def __init__(self, parent = None):
QDialog.__init__(self, parent)
self.setupUi(self)
#pyqtSignature("")
def on_btn_get_ref_dir_released(self):
print '2nd form'
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent = None):
"""
Constructor
"""
QMainWindow.__init__(self, parent)
self.setupUi(self)
self.prefs = QDialog()
ui = Ui_prefs()
ui.setupUi(self.prefs)
#pyqtSignature("")
def on_btn_process_released(self):
print 'process'
no_of_shots = self.spn_no_of_shots.value()
#spot_path = self.le_proj_path.text()+'\\'+self.lst_spots.selectedItems()[0].text()
spot_path_string = 'self.le_proj_path.text())+os.sep,'
spot_path_string += str(self.lst_spots.selectedItems()[0].text())
os.path.join(spot_path_string)
save_position = self.lst_spots.selectedItems()[0]
if no_of_shots > 0:
if self.cmb_mode.currentText() == 'Create Shots':
print ('creating shots')
for x in range(1, no_of_shots+1):
make_render_folders.create_shot_folder(spot_path, x)
else:
print ('inserting shots')
if self.lst_shots.count() > 0:
t =self.lst_shots.selectedItems()[0].text()
cur_shot = t[2:]
cur_shot_val = int(cur_shot)
cur_index = self.lst_shots.currentRow()
print('sel_value :'+cur_shot)
print('sel_index :'+str(cur_index))
next_shot_index = int(cur_index)+1
print('nextshot_index ='+str(next_shot_index))
next_shot_text = self.lst_shots.item(next_shot_index).text()
next_shot_val = int(next_shot_text[2:])
print('nextshot value ='+str(next_shot_val))
insert_space = next_shot_val - cur_shot_val
print(str(insert_space))
if no_of_shots > (insert_space-1) :
print "not enough space - please reduce shots to insert"
else:
print('insert the shots')
for x in range(cur_shot_val,(cur_shot_val+no_of_shots) ):
print (str(x))
make_render_folders.insert_shot_folder(spot_path, x+1)
make_render_folders.populate_shot_list(self)
self.lst_shots.setCurrentRow(0)
#pyqtSignature("")
def on_btn_quit_released(self):
print 'quit'
self.close()
#pyqtSignature("")
def on_cmb_mode_currentIndexChanged(self, ret_text):
print ret_text
#pyqtSignature("")
def on_le_proj_path_editingFinished(self):
print "editingFinished le_proj_path"
def on_le_new_spot_name_textChanged(self):
if len(self.le_new_spot_name.text()) > 0 :
self.btn_add_spot.setEnabled(True)
else :
self.btn_add_spot.setEnabled(False)
#pyqtSignature("")
def on_btn_add_spot_released(self):
v_NewSpotFolder = self.le_new_spot_name.text()
v_rPath = self.le_proj_path.text()
x = make_render_folders.create_spot_folder(v_NewSpotFolder,v_rPath)
if x :
self.le_new_spot_name.clear()
make_render_folders.populate_spots_list(self)
#pyqtSignature("")
def on_actionLoad_Project_triggered(self):
print "actionLoad_Project"
self.lst_shots.clear()
self.lst_spots.clear()
proj_dir = str(QFileDialog.getExistingDirectory(self, 'Select Project'))
print proj_dir
if os.path.exists(proj_dir):
print 'the file is there'
elif os.access(os.path.dirname(proj_dir), os.W_OK):
print 'the file does not exists but write privileges are given'
else:
print 'can not write there'
p = os.path.join(proj_dir+os.sep,'renders')
self.le_proj_path.setText(p)
make_render_folders.populate_spots_list(self)
#pyqtSignature("")
def on_actionConfig_triggered(self):
print "config"
self.prefs.show()
#pyqtSignature("")
def on_actionQuit_triggered(self):
print "ActionQuit"
#pyqtSignature("")
def on_btn_get_folders_released(self):
make_render_folders.populate_spots_list(self)
def on_lst_spots_itemClicked (self):
print "got you"
self.lst_shots.clear()
make_render_folders.populate_shot_list(self)
Thanks !!!
Event handling for the Prefs window can go anywhere. Personally I always prefer to give each window its own file.
So the py files would look something like:
Mainwindow.py (Main form events etc)
Ui_make_render_folders.py (Qt Designer generated UI)
prefs.py (Prefs form events etc)
ui_prefs.py (Qt Designer generated UI)
your other files...
Initialisation: (MainWindow.py)
from PyQt4.QtGui import QMainWindow
from Ui_make_render_folders import Ui_MainWindow
#prefs here refers to the file: prefs.py. PrefWindow is the class within this file.
from prefs import PrefWindow
class MainWindow(QMainWindow):
def __init__(self, parent=None):
#Better way to initialise
super(MainWindow, self).__init__(parent)
self.main = Ui_MainWindow()
self.main.setupUi(self)
#initialise prefs to variable
self.prefs = PrefWindow(self)
# Rest of init...
Note: initialising of second form will be similar to above.
You should place all events for prefs form within its class.
From the function that calls Prefs:
#pyqtSlot()
def on_actionConfig_triggered(self):
print "config"
self.prefs.exec_() # Will block main window while its open.
# Use self.prefs.show() if you want user to be able to interact with main window
Finally: pyqtSignature
On http://pyqt.sourceforge.net/Docs/PyQt4/old_style_signals_slots.html it states that:
'The QtCore.pyqtSignature() serves the same purpose as the pyqtSlot() decorator but has a less Pythonic API.'
Better to use pyqtSlot()

pyqt4 - singleapplication - bring up the original window in an attempt to open the app for the second time

I want only one instance of my app to be running at each time. but when the user attempts to open it the second time, I want the first window to be brought to the front (it could be just minimized or minimized to the corner of the taskbar and the user doesn't know how to open it)
I have this code that does the detection job and it doesn't allow the second instance. I have trouble with the part that it has to open the original window. I have commented out some of my attempt.
import sys
from PyQt4 import QtGui, QtCore
import sys
class SingleApplication(QtGui.QApplication):
def __init__(self, argv, key):
QtGui.QApplication.__init__(self, argv)
self._activationWindow=None
self._memory = QtCore.QSharedMemory(self)
self._memory.setKey(key)
if self._memory.attach():
self._running = True
else:
self._running = False
if not self._memory.create(1):
raise RuntimeError(
self._memory.errorString().toLocal8Bit().data())
def isRunning(self):
return self._running
def activationWindow(self):
return self._activationWindow
def setActivationWindow(self, activationWindow):
self._activationWindow = activationWindow
def activateWindow(self):
if not self._activationWindow:
return
self._activationWindow.setWindowState(
self._activationWindow.windowState() & ~QtCore.Qt.WindowMinimized)
self._activationWindow.raise_()
self._activationWindow.activateWindow()
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.label = QtGui.QLabel(self)
self.label.setText("Hello")
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.label)
if __name__ == '__main__':
key = 'random _ text'
app = SingleApplication(sys.argv, key)
if app.isRunning():
#app.activateWindow()
sys.exit(1)
window = Window()
#app.setActivationWindow(window)
#print app.topLevelWidgets()[0].winId()
window.show()
sys.exit(app.exec_())
I've made this work on Windows using the win32 api (I'm not entirely sure, but there are probably equivalent calls on macos/unix).
Add the following import to your application,
import win32gui
set the window title to a fixed name (instead of doing this, you could store its whndl in the shared memory)
window = Window()
window.setWindowTitle('Single Application Example')
window.show()
and then change your activateWindow method to something like the following:
def activateWindow(self):
# needs to look for a named window ...
whndl = win32gui.FindWindowEx(0, 0, None, "Single Application Example")
if whndl is 0:
return #couldn't find the name window ...
#this requests the window to come to the foreground
win32gui.SetForegroundWindow(whndl)
You might be interested by the solutions proposed here
For instance, I would try:
app = SingleApplication(sys.argv, key)
if app.isRunning():
window = app.activationWindow()
window.showNormal()
window.raise()
app.activateWindow()
sys.exit(1)
window = Window()
app.setActivationWindow(window)
window.setWindowFlags(Popup)
window.show()

Python code to change displayed image with pyqt4 on request

I have the following code to display am image using pyQt:
app = QtGui.QApplication(sys.argv)
window = QtGui.QMainWindow()
window.setGeometry(opts.posx, opts.posy, opts.width, opts.height)
pic = QtGui.QLabel(window)
pic.setGeometry(5, 5, opts.width-10, opts.height-10)
pixmap = QtGui.QPixmap(opts.filename)
pixmap = pixmap.scaledToHeight(opts.height)
pic.setPixmap(pixmap)
window.show()
sys.exit(app.exec_())
I would like to wrap up this code possibly in the form of a class, and be able to set a different image during runtime, using signals, socket, threads I really do not know. I would imagine something like:
class MyImage(object):
def __init(self, args):
some setup code
self.pic = whatever
def set_image(self, filename):
pixmap = QtGui.QPixmap(opts.filename)
pixmap = pixmap.scaledToHeight(opts.height)
pic.setPixmap(pixmap)
With the original code I just call sys.exit(app.exec_()) which makes the code 'freeze'. But I want to send a signal (and a filename) from a different running python code. Any suggestion how this can be handled easily and straightforward? Maybe overwriting the app.exec_ method?
Something like this should work for you:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4 import QtGui, QtCore
class ImageChanger(QtGui.QWidget):
def __init__(self, images, parent=None):
super(ImageChanger, self).__init__(parent)
self.comboBox = QtGui.QComboBox(self)
self.comboBox.addItems(images)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.comboBox)
class MyWindow(QtGui.QWidget):
def __init__(self, images, parent=None):
super(MyWindow, self).__init__(parent)
self.label = QtGui.QLabel(self)
self.imageChanger = ImageChanger(images)
self.imageChanger.move(self.imageChanger.pos().y(), self.imageChanger.pos().x() + 100)
self.imageChanger.show()
self.imageChanger.comboBox.currentIndexChanged[str].connect(self.changeImage)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.label)
#QtCore.pyqtSlot(str)
def changeImage(self, pathToImage):
pixmap = QtGui.QPixmap(pathToImage)
self.label.setPixmap(pixmap)
if __name__ == "__main__":
import sys
images = [ "/path/to/image/1",
"/path/to/image/2",
"/path/to/image/3",
]
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow(images)
main.show()
sys.exit(app.exec_())

Resources