pyqgis(pyqt5,QGIS3 plugin) mouse clicked popup and get the point x,y - pyqt5

I used this code in qgis2 plugin. But I cannot use in qgis3. The error code is no connect lib in QObject. How can I solve or change my code?
def handleMouseDown(self, point, button):
global coor_x, coor_y, width
width = round(self.iface.mapCanvas().mapUnitsPerPixel() * 2, 4)
coor_x = round(point.x(), 4)
coor_y = round(point.y(), 4)
self.dlg.show()
def run(self):
self.pointEmitter = QgsMapToolEmitPoint(self.iface.mapCanvas())
QObject.connect(self.pointEmitter, SIGNAL("canvasClicked(const QgsPoint, Qt::MouseButton)"), self.handleMouseDown)
self.iface.mapCanvas().setMapTool(self.pointEmitter)

Related

wxPython using variable to reference an object

I am trying to pass the name of an object into a function so that I can manipulate it from within a function. I have about 14 radio buttons that I want to change depending on what is clicked. Using the function cuts down on code reuse.
However the object tries to use the name of the variable as the object name, not the string contained within.
Is it possible to use this string to refer to the object name compared to the string itself?
self.myObject.GetValue():
self.writeLine('hello','myObject')
self.myObject2.GetValue():
self.writeLine('test','myObject2')
def writeLine(self,str, objName):
print str
self.objName.Enable(False)
self.objName.SetValue(False)
self.objName.Enable(False)
AttributeError: 'myProg' object has no attribute 'objName'
Ignoring the obvious errors in the posted code because you wouldn't be able to run it, if your actual code was as stated, you can pass the object rather than a string.
Then in the function use the object passed not self.objName which doesn't exist!
Here's an utterly pointless piece of code to demonstrate:
import wx
class ButtonFrame(wx.Frame):
def __init__(self, value):
wx.Frame.__init__(self,None)
self.btn1 = wx.Button(self, -1, ("Button A"))
self.btn2 = wx.Button(self, -1, ("Button B"))
self.btnSizer = wx.BoxSizer(wx.HORIZONTAL)
self.btnSizer.Add(self.btn1 , 0, wx.RIGHT, 10)
self.btnSizer.Add(self.btn2 , 0, wx.RIGHT, 10)
self.btn1.Bind(wx.EVT_BUTTON, self.OnPressA)
self.btn2.Bind(wx.EVT_BUTTON, self.OnPressB)
self.SetSizer(self.btnSizer)
self.Centre()
self.Show()
self.btn1.Enable(False)
mystr = self.btn1.GetLabel()
self.writeLine(mystr,self.btn1)
self.writeLine('test',self.btn2)
def writeLine(self,str, objName):
print(str)
x = objName.IsEnabled()
objName.Enable(not x)
def OnPressA(self,evt):
self.btn1.SetLabel('Button C')
self.Layout()
def OnPressB(self,evt):
self.btn2.SetLabel('Button D')
self.Layout()
if __name__ == "__main__":
app = wx.App(False)
ButtonFrame(None)
app.MainLoop()
Initially, as you can see button A is disabled button B is enabled.
The result should be:
Button A is Enabled
Button B is Disabled
Printed on the command line should be Button A followed by test

wxWidgets TaskBarIcon as drop target

How can I define the tray icon as a drop target using wxWidgets?
SetDropTarget is neither available in the TaskBarIcon nor in the Icon class.
I would like to have something like:
class TextDropTarget(wx.TextDropTarget):
def __init__(self, obj):
wx.TextDropTarget.__init__(self)
self.obj = obj
def OnDropText(self, x, y, data):
self.obj.action(data)
class TaskBarIcon(wx.adv.TaskBarIcon):
def __init__(self):
super().__init__()
self.SetIcon(wx.Icon(wx.Bitmap(TRAY_ICON)), TRAY_TOOLTIP)
self.SetDropTarget(TextDropTarget(self))
def action(self, data):
# Do something
After some research I can answer the question myself:
On Mac this is possible, because it's a common feature on this OS.
On Windows and Linux this is possible through an ugly workaround. I'm wondering why this is not supported on these OS, because there are indeed some use cases for this feature.
Here is the workaround (which is also mentioned here) for Windows:
Get the system tray area:
def FindSysPagerWindow():
hWnd = win32gui.FindWindowEx(win32gui.GetDesktopWindow(), 0, "Shell_TrayWnd", None)
if hWnd:
hWnd = win32gui.FindWindowEx(hWnd, None, "TrayNotifyWnd", None)
if hWnd:
hWnd = win32gui.FindWindowEx(hWnd, None, "SysPager", None)
return hWnd
class TaskBarIcon(wx.adv.TaskBarIcon):
def __init__(self, r):
super().__init__()
hSysPager = FindSysPagerWindow()
# Get rectangle of system area
self.region = win32gui.GetWindowRect(hSysPager)
self.frm = None
self.source = None
Create a thread which detects drag start events and creates a transparent frame above the system tray:
def callback(self, hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime):
length = user32.GetWindowTextLengthW(hwnd)
title = ctypes.create_unicode_buffer(length + 1)
user32.GetWindowTextW(hwnd, title, length + 1)
if self.frm is None and (title.value == "Drag"):
self.source = GetProcessFilename(GetProcessId(dwEventThread, hwnd))
self.frm = SystemTrayFrame(self.region, self.onDrop)
def DragDetectThread(self):
ole32.CoInitialize(0)
WinEventProc = WinEventProcType(self.callback)
user32.SetWinEventHook.restype = ctypes.wintypes.HANDLE
hookId = user32.SetWinEventHook(win32con.EVENT_OBJECT_SHOW, win32con.EVENT_OBJECT_SHOW,
0, WinEventProc, 0, 0, win32con.WINEVENT_OUTOFCONTEXT)
msg = ctypes.wintypes.MSG()
while user32.GetMessageW(ctypes.byref(msg), 0, 0, 0) != 0:
user32.TranslateMessageW(msg)
user32.DispatchMessageW(msg)
user32.UnhookWinEvent(hookId)
ole32.CoUninitialize()
A still unresolved problem here is that the detection of the correct drag source is not working reliable. If the mouse is moved too fast at the drag start, then the detected source may be wrong. But this is only a problem if this information is important.
Create a listener for mouse button events, using pynput, to detect left mouse button up event, which is interpreted as a drag end event. The listener and also the onDrop method destroy the transparent frame:
from pynput.mouse import Listener, Button
...
self.listener = Listener(on_click=self.onMouseButtonEvent)
self.listener.start()
def onMouseButtonEvent(self, x, y, button, pressed):
if self.frm is not None and (button == Button.left) and not pressed:
self.frm.Destroy()
self.frm = None
def onDrop(self, x, y, data):
# Do something with the dropped data
if self.frm is not None:
self.frm.Destroy()
self.frm = None
The class for the transparent frame looks something like this:
class SystemTrayFrame(wx.Frame):
def __init__(self, r, cbDrop):
super().__init__(None, wx.ID_ANY, "TransparentFrame", pos=(r[0], r[1]), size=(r[2] - r[0], r[3] - r[1]),
style=wx.STAY_ON_TOP)
dropTarget = DropTarget(cbDrop)
self.SetDropTarget(dropTarget)
self.SetTransparent(0)
self.Show()
Up to here this is all fine if it is ok that the whole system tray area is the drop target for your application. But if you want to limit the drop area to your system tray icon you need now the ugly workaround:
a) Replace your fancy system tray icon with a uniquely colored icon with a shape which you can easily detect.
b) Make a screenshot of the system tray area, replace the system tray icon back to your fancy application icon, then search for the position of your uniquely colored icon:
im = ImageGrab.grab(bbox=self.region)
# Search for icon position and size (because of optional scaling by OS)
The search operation can be a bit more complicated when scaling was enabled by the OS.
c) Use this result for positioning the transparent frame.
Hope this helps other people who are running into the same problem.

WxPython Pango error after clicking on wx.SpinCtrl

The actual error is produced by a much larger program I've been writing, but the following sample reproduces the error:
import wx
class MyLine(wx.Frame):
def __init__(self):
self.thickness = 1
self.length = 10
self.spin_ctrl = []
super(MyLine, self).__init__(None)
self.SetBackgroundColour(wx.ColourDatabase().Find("GREY"))
vbox = wx.BoxSizer(wx.VERTICAL)
#Length section
self.spin_ctrl.append(wx.SpinCtrl(self, initial = self.length, min = 1, max = 100))
vbox.Add(self.spin_ctrl[-1], 0, wx.ALL | wx.ALIGN_CENTER, 5)
#Thickness section
self.spin_ctrl.append(wx.SpinCtrl(self, initial = self.thickness, min = 1, max = 10))
vbox.Add(self.spin_ctrl[-1], 0, wx.ALL | wx.ALIGN_CENTER, 5)
self.SetSizerAndFit(vbox)
self.Show()
app = wx.App()
fr = MyLine()
app.MainLoop()
When the above is run, a window appears with two SpinCtrl buttons. If I click on the first one to change the value and then close the window, everything works fine and there are no error messages. When I click on the second button to change its value and then close the window, the following error appears:
Pango-CRITICAL **: pango_layout_get_cursor_pos: assertion 'index >= 0 && index <= layout->length' failed. Is this a bug or am I not using SpinCtrl buttons correctly?
I am running WxPython4.0.3.

QCombobox bigger in size for the first time only

class RangeSelection(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
layout = QGridLayout(self)
self.setLayout(layout)
self._create_widgets()
layout.addWidget(self.select_combo, 1, 1)
layout.addWidget(self.stacked, 1, 2, 5, 1)
self.stacked.currentWidget().setSizePolicy(
QSizePolicy.Preferred, QSizePolicy.Preferred)
self.stacked.currentChanged.connect(self.onCurrentChanged)
def onCurrentChanged(self):
currentw = self.stacked.currentWidget()
currentw.adjustSize()
if currentw == self.releasew:
currentw.sizeAdjustPolicy = QComboBox.AdjustToContentsOnFirstShow
self.adjustSize()
def _create_widgets(self):
self.stacked = QStackedWidget()
self.datew = QCalendarWidget()
self.datew.setVerticalHeaderFormat(QCalendarWidget.
NoVerticalHeader)
self.stacked.addWidget(self.datew)
self.buildidw = QLineEdit()
self.stacked.addWidget(self.buildidw)
self.releasew = QComboBox()
self.releasew.addItems([str(k) for k in sorted(releases())])
self.stacked.addWidget(self.releasew)
self.revw = QLineEdit()
self.stacked.addWidget(self.revw)
self.select_combo = QComboBox()
self.select_combo.addItems(['date', 'buildid', 'release', 'changeset'])
self.select_combo.activated.connect(self.stacked.setCurrentIndex)
I have this code where I am having four widgets in the QStackedWidget. When I run this code and change my selection in self.select_combo from date to release, the self.releasew combobox initially shows up as same size as that of the QCalendarWidget( which obviously looks horrible ). But, when I change my selection from release to any other value and then back to release, the self.releasew combobox shows up in the size it should. Why is this happening? What is the solution to this problem?
Note: I am using PyQt4. Also note that widgets for buildid and changeset do not show any abnormal behaviour.
I removed the setSizePolicy and sizeAdjustPolicy code. I also removed the call to self.adjustSize(). This worked. Though, I don't know why.

How do I store an integer input from the user into a variable in Tkinter?

For my Python class were using turtle graphics.
We have too draw a target that appears at a random location on the screen. Got that.
Then a pop up window appears asking for what you think the coordinates of the target are. First the pop up box asks you to enter the x coordinate then it asks you to enter the y coordinate.
I'm having trouble saving the users inputed integers from my Tkinter window into variables I can use later in the program.
from Tkinter import *
window = Tk()
window.title("Player Input")
window.geometry('+350+130')
thexinput = IntVar()
L1 = Label(window, text="Enter the x coordinate for Mike")
L1.pack( side = LEFT)
E1= Entry(window, textvariable= thexinput, bd =5)
E1.pack(side = RIGHT)
def userinput():
global inp
a = raw_input(thexinput.get())
inp = a
b = Button(window, text = 'Submit', command = userinput)
b.pack(side = BOTTOM)
window.mainloop()
You don't need to use raw_input, you just need to call the get method of the entry widget.
a = thexinput.get()