Highlight Select Box in Python - python

I am trying to rebuild the functionality of the desktop's "highlight to select" feature so that I can use it in my own app. When I say "highlight to select" I mean the selection box that shows up if you click and drag on your desktop (native to all main-stream OS).
I've been working for hours trying to recreate it, and simply can't find a way. I've tried PyGTK, Xlib for python, and a couple other weird hacks. All of which have their own problems that won't allow me to move forward.
I generally don't ask for straight up example code without providing some sort of starting point, but in this project I don't even know where to start. How would you do this?
Here's the requirements:
Must draw on the root window (or a transparent layer that "appears" to be the root)
Must return the coordinates of the selection (x, y, height width)
Update: Forgot some details.
I am using Ubuntu 10.10
I have dual monitors (though, I don't think that should matter)
I don't mind downloading any extra libraries that are necessary

I don't know if this is what you're looking for, but what if you created another window in your module, and have your code show it when you release drag? You could fetch the cursor's current position, and have it draw the window there.
This should help you get the mouse position on the root window.
So, your code may look a little like this (this is untested code!) I'm only showing the relevant portions of what goes inside __ init __.
def __init__(self):
...
#Some of your code here.
...
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
#Note that I am creating a popup window separately.
popwin = gtk.Window(gtk.WINDOW_POPUP)
#I am setting "decorated" to False, so it will have no titlebar or window controls.
#Be sure to compensate for this by having another means of closing it.
popwin.set_decorated(False)
def ShowPopup():
#You may need to put additional arguments in above if this is to be an event.
#For sake of example, I'm leaving this open ended.
#Get the cursor position.
rootwin = widget.get_screen().get_root_window()
curx, cury, mods = rootwin.get_pointer()
#Set the popup window position.
popwin.move(curx, cury)
popwin.show()
def HidePopup():
#This is just an example for how to hide the popup when you're done with it.
popwin.hide()
...
#More of your code here.
...
#Of course, here is the code showing your program's main window automatically.
win.show()
A very simplistic approach, but it should give the appearance of what you're wanting.

Related

Add side panel without resizing parent

I'm using PySide2 to write a gui (witout QtDesigner).
The mainwindow contains plots and some other widgets. Through a menu option I want to open a side panel widget. The way I want it to work is that the whole window simply grows to contain that new widget without changing the size of anything else in the main window. How can this be done?
Currently the widget is just added to the central layout with addWidget, I've also tried making it a QDockWidget but it is still resized (and anyway I would like to avoid the extra fluff that comes with having a DockWidget).
So I have
---------
|content|
---------
which should turn into
-------------
|new|content|
-------------
but currently I get
---------
|new|cnt|
---------
It's hard to do well on the "client" side of Qt; this would really belong within Qt itself. I have implemented a slightly more general variant of this a couple of years ago, and just to make it work well across Windows, Mac and KDE, the code ballooned to over a thousand lines to cover all the icky corner cases, with another thousand for the test harness. It was surprisingly hard to implement the tests - especially on X11, where there was no way around using native X APIs to verify intended behavior. I got rid of that monstrosity soon later - the effort was unnecessary.
You can have the side panel as a separate top-level frameless widget that moves itself so that its top-right corner is aligned with the top-left corner of the content window, and resizes itself vertically to match the vertical size of the content window. You can of course make it slightly shorter (vertically) while still center-aligning it vertically with the content window.
You'd want to capture the resize events of the content window to do this: the side panel should install itself as an event filter for the content window.
You'll want the side panel to be a Qt child of the content window, but you also need to make it a top-level window, i.e. set the Qt::Window flag on it, so that it becomes top-level and not a sub-widget of the content window.

Changing background color (bgc) of windows in Maya?

I worked somewhere in the past where since we had multiple sessions of Maya open, the background color could be randomly changed so when you switched from a session quickly it was easy to sort out which window belonged to what Maya session.
And so far, I can change the bgc of the main UI by using:
window -e bgc 0.5 0.5 0.5 $gMainWindow;
After searching for other global variables, I found $AllWindows, $CommandWindow, among others since the docs state that 'bgc' is a windows only flag. I can not get any of the colors to change on any window besides the $gCommandWindow, which popped up and I do not recall seeing it before.
I'm hoping to at least change the Script Editor window in addition to MainWindow if anyone knows whether it's possible or not? It is not mission critical, but now I'm interested in seeing if it can be done.
Thanks!
Since Maya's interface is using Qt, you can use the power of PySide to tweak any widget you want. Usually the only tricky part is actually finding the proper widget to modify.
Here's how you can tweak the Script Editor to give it a yellow border:
import shiboken2
from maya import cmds
from maya import OpenMayaUI
from PySide2 import QtWidgets
panels = cmds.getPanel(scriptType="scriptEditorPanel") # Get all script editor panel names.
if panels: # Make sure one actually exists!
script_editor_ptr = OpenMayaUI.MQtUtil.findControl(panels[0]) # Grab its pointer with its internal name.
script_editor = shiboken2.wrapInstance(long(script_editor_ptr), QtWidgets.QWidget) # Convert pointer to a QtWidgets.QWidget
editor_win = script_editor.parent().parent().parent().parent() # Not very pretty but found that this was the best object to color with. Needed to traverse up its parents.
editor_win.setObjectName("scriptEditorFramePanel") # This object originally had no internal name, so let's set one.
editor_win.setStyleSheet("#scriptEditorFramePanel {border: 3px solid rgb(150, 150, 45);}") # Set its styleSheet with its internal name so that it doesn't effect any of its children.
OpenMayaUI.MQtUtil gives you the awesome ability to find any control by name, so as long as you know the name of the widget you want to modify, you can find it (tough part is finding it sometimes!). In this case I had to traverse up a few parents to find one that worked best to outline the whole window. You can fool around with this and color, let's say, only the text area. And since this is PySide's style sheets you can do whatever your heart desires, like effect the background color, the thickness of the outline, and so on.
Since we're only effecting the style sheet this also won't save with the preferences and will revert to what it was on a new session.
Hope that helps.

Tkinter - how do I teleport mouse pointer [duplicate]

I have a 3d rendering program that rotates the world around an observer based on the position the mouse is on the screen. The amount in radians that the world is rotated is defined by this line
glob.worldx=-(w.winfo_pointerxy()[0]-xy[0])/250
Where xy[0] is the x coordinate of the center of the screen
This means that the amount that the observers field of view can be rotated is limited by the distance the mouse can go. If I could get the mouse to come back to the center of the screen I could solve this problem. Any ideas?
The good news is that there is a way to do it.
The intermediate news is that it's not well documented.
The bad news is that it only works on some platforms.
The other intermediate news is that you can step outside of Tk on at least some platforms.
The way to do this in Tcl/Tk is by generating a <Motion> event with -warp 1. The documentation on this is sparse, and scattered around a few different pages (start at bind), but the details are described here. Basically, it's just this:
event generate . <Motion> -warp 1 -x 50 -y 50
So, how do you do this from Tkinter?
Well, event_generate isn't documented anywhere, and neither is the <Motion> event, or the warp parameter… but it's pretty simple to figure out if you know how Tk maps to Tkinter:
window.event_generate('<Motion>', warp=True, x=50, y=50)
And this does indeed generate an event, as you can see by binding <Motion>. Here's a simple test program:
from tkinter import *
root = Tk()
def key(event):
root.event_generate('<Motion>', warp=True, x=50, y=50)
def motion(event):
print('motion {}, {}'.format(event.x, event.y))
root.bind('<Key>', key)
root.bind('<Motion>', motion)
root.mainloop()
Run it, click the window to make sure it has focus, move the cursor around, and you'll see it print out something like this:
motion 65, 69
motion 65, 70
motion 65, 71
Then hit a key, and it'll print out this:
motion 50, 50
Which is great… except that it may not actually be able to move your cursor, in which case all this does is trick Tk into thinking the cursor moved.
From skimming various forums, it looks like:
Mac: Does not work.
You must have Tk 8.6.something or later (see issue 2926819). And you probably have 8.5.something.
But it's not hard to go right to the Cocoa API.
Windows: Usually works.
You must have Tk 8.4.something or later. I couldn't find the bug for this, but you can count on 8.4 with any official Windows binary install of Python 2.7 or 3.x+.
You also must not be running a full-screen app (which you generally aren't, with Tk).
On Vista and later, in some cases it won't work. This may have something to do with not owning the desktop session or not being a local console session, or it may have to do with needing Administrator or other privileges.
If it doesn't work, it's easy to go right to the Win32 API.
X11 (most linux, *BSD, etc.): Usually
Your window manager must not have disabled other clients from warping the pointer. Fortunately, that doesn't seem to be a common thing to do.
If you have this problem, there's no way around it.
Other platforms (iOS, Android, etc.): No idea.
For Mac, you want to generate and send an NSMouseMoved event. The easy way to do this is with pyobjc (which is built in if you're using Apple's Python; otherwise you have to install it):
app = Foundation.NSApplication.sharedApplication()
event = Foundation.NSEvent.mouseEventWithType_location_modifierFlags_timestamp_windowNumber_context_eventNumber_clickCount_pressure_(
Foundation.NSMouseMoved, (50, 50), 0, 0,
app.mainWindow().windowNumber(), None, 0, 0, 0.0)
app.sendEvent_(event)
For Windows, you want to call the SetCursorPos API, or generate and send a MOUSEEVENT. The former will not work with, e.g., DirectX games; the latter may not work with remote desktops. For this case, you probably want the former. Either way, the easiest way to do this is to install pywin32, and then it's just:
win32api.SetCursorPos((50, 50))
For anyone interested in moving the cursor to an absolute position on the screen (using #abarnert's tkinter method):
# Moves the mouse to an absolute location on the screen
def move_mouse_to(x, y):
# Create a new temporary root
temp_root = tk.Tk()
# Move it to +0+0 and remove the title bar
temp_root.overrideredirect(True)
# Make sure the window appears on the screen and handles the `overrideredirect`
temp_root.update()
# Generate the event as #abarnert did
temp_root.event_generate("<Motion>", warp=True, x=x, y=y)
# Make sure that tcl handles the event
temp_root.update()
# Destroy the root
temp_root.destroy()
This function shouldn't interfere with other tkinter windows. Please note that this function creates a new window that is refreshed twice so it might be slow and the user might notice the window created.

Python + Qt -> 2D overlay over other full screen app?

Backgroud:
I play Grand Strategy Games a lot. Part of addcition comes out of AARs (After Action Reports). Stories players write about games. Ofc. that require save games cause "quick" game is longer than 10 hours.
Problem:
Game do not support automatic save games other than 3 autosaves that get overridden every time. So I want to write app that will use Qt for tracking file changes. Every time game auto saves, this app will rename and move to choosen location savegame.
But since its full screen game, players may forgot to turn on my app I need way to indicate state of my app.
Question:
How can I make 2D overlay over portion of full screen 3D app, given that I use Python and Qt?
Alternative
I do not think that sound warnings would solve my problem, since it would work if someone forgotten to choose save game to track, but it would not work if someone completely forgot to turn on my app. While lack of icon would be enough to inform about such mistake.
But if you can find any other way to indicate that my app is not turned on or configured, post your ideas in answers.
Very old post, but I will let this reply here for others looking for something similar.
Check PyWinCtl. If you are able to build a frameless, semi-transparent window using Python and Qt, alwaysOnTop(True) and acceptInput(False) methods will make the trick, but only with games based on CGI calls, not if they use DirectDraw Exclusive Mode (for this, I'm also searching for a Python solution, this is why I ended here!)
In the meantime, you can try this on Qt when initializing your window (it's a piece of code, not reproduceble, sorry):
# Make it transparent to input
self.setFocusPolicy(QtCore.Qt.NoFocus)
if "Linux" in platform.platform():
self.setAttribute(QtCore.Qt.WA_X11DoNotAcceptFocus, True)
self.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents, True)
self.setAttribute(QtCore.Qt.WA_InputMethodTransparent, True)
self.setAttribute(QtCore.Qt.WA_ShowWithoutActivating, True)
# Make it semi-transparent
self.setWindowOpacity(128)
# Setting flags: on top, frameless and no focus
self.setWindowFlags(QtCore.Qt.Tool| QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowDoesNotAcceptFocus)
I think the name of the functions and parameters are self-explicative (let me know if they are not).
The part of making your window transparent to input methods is important, otherwise the window can be eventually clicked and the game will lose focus. This effect on the window is permanent.
The part of making your window semi-transparent will not work as is. You need to implement a custom paintEvent for your window, like this (again, not reproducible, sorry):
def paintEvent(self, event=None):
# This is required to draw a semi-transparent window
# https://stackoverflow.com/questions/33982167/pyqt5-create-semi-transparent-window-with-non-transparent-children
painter = QtGui.QPainter(self)
painter.setOpacity(0.4) # or your desired opacity level
painter.setBrush(QtCore.Qt.black) # or your desired background color
painter.setPen(QtGui.QPenQtCore.Qt.black)) # or your desired background color
painter.drawRect(self.rect())
The part of making it always on top might not be permanent, this is why using pywinctl's alwaysOnTop() is recommended, furthermore, it's better to recall it, for instance every second, to bring your window back to top in case it's obscured by any reason.

Customize PyQt multi-touch pan gestures on QListWidget

I'm having a problem with a simple Notepad application I'm writing to teach myself basic Python/PyQt.
Specifically, I want to know how to change the multi-touch pan gesture sensitivity on a QListWidget.
As it is now, when I drag up and down with 2 fingers, it seems like the list is moving up/down one step for each pixel I move with my fingers. This is nothing I've implemented myself, it seems to work out of the box for list widgets
I want the movement to mimic the speed of my fingers i.e one move up/down of the widget items for every x*height_of_item_in_pixels. Is this doable without major hacking into the gesture system?
How would I go about this?
I'm using PyQt 4.8.3 with Python 2.6
Quite a bit late, but maybe I can help others who stumble upon this question:
The solution is to set the scroll mode for the QListWidget to ScrollPerPixel (instead of the default ScrollPerItem):
list_widget.setVerticalScrollMode(list_widget.ScrollPerPixel)
A minimal example:
import sys
from PyQt5 import QtWidgets
# create app
app = QtWidgets.QApplication(sys.argv)
# create list widget
list_widget = QtWidgets.QListWidget()
# populate list widget with dummy items
for index in range(100):
list_widget.addItem(QtWidgets.QListWidgetItem('item {}'.format(index)))
# THIS IS THE IMPORTANT PART: set the scroll mode
list_widget.setVerticalScrollMode(list_widget.ScrollPerPixel)
# OPTIONAL: enable pan by mouse (and one-finger pan)
QtWidgets.QScroller.grabGesture(list_widget.viewport(),
QtWidgets.QScroller.LeftMouseButtonGesture)
# show the list widget
list_widget.show()
# run the event loop
app.exec_()
Note: This example also shows how to implement panning using the mouse, which is useful for testing on devices without touch-screen. Moreover, the mouse pan setting also enables one-finger pan, in addition to the default two-finger pan gesture (at least on Windows, tested on Surface Pro tablet).
More than likely this functionality exists because the multitouch event is being interpreted by your operating system as a scroll of some type and QT is recieving the scroll action, rather than the multitouch action directly.

Categories

Resources