Drag n drop Cursor issue in pygtk for windows - windows

I'm writing an interface using pygtk under linux. I've got a tiny very minute and minor issue when trying my program under windows (XP). It's ridiculous but I'm getting a bit obsessed, and some people around me siad I should talk to someone, so here I am.
I'm doing drag n' drop on some EventBoxes containing Images. The idea is to set the drag icon to the Image in the EventBox through a Pixbuf. No problem with set_icon_pixbuf under Linux, works perfectly fine.
Under Windows XP (I unfortunately don't have anything more recent) when the icon covers the cursor hotpoint, the drop doesn't occur anymore. Is there any workaround ? I haven't seen anything similar on google, so I'm trying my luck here.
I've written a code snippet that reproduces this behaviour, if anyone wants to give it a try.
import gtk
TARGET_TYPE_BT = 0
BUTTON = [('button', gtk.TARGET_SAME_APP, TARGET_TYPE_BT)]
def on_drag_begin(widget, context) :
context.set_icon_pixbuf(pixbuf, size / 2, size / 2)
def on_drag_data_get(widget, context, selec, targ, time) :
selec.set( selec.target, 8, widget.get_label())
def on_drag_data_received(widget, context, x, y, selec, targ, time) :
widget.set_label(selec.data)
size = 48
pixbuf = gtk.gdk.Pixbuf( gtk.gdk.COLORSPACE_RGB, True, 8, size, size)
pixbuf.fill(0xff7777ff)
buttons = [gtk.Button('Spam'), gtk.Button('Spam, spam, egg and spam')]
hbox = gtk.HBox(True)
for button in buttons :
button.connect('drag_begin', on_drag_begin)
button.connect('drag_data_get', on_drag_data_get)
button.connect('drag_data_received', on_drag_data_received)
button.drag_source_set(gtk.gdk.BUTTON1_MASK, BUTTON, gtk.gdk.ACTION_MOVE)
button.drag_dest_set(gtk.DEST_DEFAULT_ALL, BUTTON, gtk.gdk.ACTION_MOVE )
hbox.pack_start(button)
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
win.connect("destroy", lambda w: gtk.main_quit())
win.add(hbox)
win.show_all()
for button in buttons :
button.window.set_cursor(gtk.gdk.Cursor( gtk.gdk.HAND1 ))
gtk.main()
In this code the size of the icon seems to have unexpected correlatives : try 24 and everything works fine !
Thanks for your attention. Any suggestions welcome.

Related

How can I make udate_idletasks work on Mac

I've written a tkinter script with animation, that works fine on Xubuntu, but when I run it on Mac, the animation doesn't work. Here's a little script that demonstrates the problem:
import tkinter as tk
from time import sleep
root = tk.Tk()
canvas = tk.Canvas(root, height=200, width = 200)
canvas.pack()
this = canvas.create_rectangle(25,25, 75, 75, fill='blue')
that = canvas.create_rectangle(125, 125, 175, 175, fill = 'red')
def blink(event):
global this, that
for _ in range(9):
canvas.itemconfigure(this, fill='red')
canvas.itemconfigure(that, fill = 'blue')
canvas.update_idletasks()
sleep(.4)
this, that = that, this
canvas.bind('<ButtonRelease-1>', blink)
root.mainloop()
This draws a red square and a blue square on a canvas. When the user clicks the canvas, the squares repeatedly switch colors. On Xubuntu, it works as intended.
On Mac, when I click the canvas, I get spinning beach ball, and after a few seconds, we see that squares have switched colors, because they switch colors an odd number of times in the code.
It seems to me that update_idletasks isn't working. Is there some way to fix this? I am running python 3.9.5 with Tk 8.6 on Big Sur.
I think what you can do is avoid tasks that will block the mainloop, in this case time.sleep(). So your code can be remade by emulating a for loop with after, and I see nothing that stops this general code from running OS independent:
count = 0 # Think of this as the `_` in for _ in range(9)
def blink(event=None):
global this, that, count
if count < 9: # Basically repeats everytime `count` is less than 9, like a for loop
canvas.itemconfigure(this, fill='red')
canvas.itemconfigure(that, fill='blue')
this, that = that, this
count += 1 # Increase count
root.after(400,blink) # Repeat this code block every 400 ms or 0.4 seconds
else:
count = 0 # After it is 9, set it to 0 for the next click to be processed
I found that using update instead of update_idletasks works on both platforms. It's my understanding though, that the latter is much preferred. See the accepted answer to this question for example. This solves my immediate problem, but does anyone know if update_idletasks ever works on the Mac?

Showing a windows with XCB / Strange Behaviour

I'm trying to show a window in xcb, inside the main window, but actually without luck.
The idea is that when the user press a button (in that case the X button) a small white window is shown (just for test).
But actually i'm stuck on that step. I watched the example code here:
http://en.wikibooks.org/wiki/X_Window_Programming/XCB
And tried to do the same in my application.
[EDIT 28/10/2013] Now with that code i can show a window, but if i try to add other variable like int i=0, or whatever, the window doesn't appear, and no expose events were raised (all events that were raised are or 0 or 2 (even if i add the variables inside other events). Any idea?
This is the XCB_KEY_PRESS event handler code:
Edit (with the new code)
case XCB_KEY_PRESS:{
xcb_key_press_event_t *kp = (xcb_key_press_event_t *)ev;
if(kp->detail==53){
printf("X pressed\n");
uint32_t vals[2];
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
vals[0]=screen->white_pixel;
vals[1]=XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS;
win = xcb_generate_id(connection);
xcb_create_window(
connection,
XCB_COPY_FROM_PARENT,
win,
root,
80,80,
150,150,
10,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
mask, values);
mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
vals[0]=screen->white_pixel;
vals[1]=0;
background=xcb_generate_id(connection);
xcb_create_gc(connection, background, win, mask, vals);
xcb_map_window(connection,win);
xcb_flush(connection);
printf("finished\n");
}
printf("KEY_PRESS - Pressed: %d\n", kp->detail);
}
root is the root window obtained from xcb_screen_t variable.
The definition of background and win are the following:
xcb_window_t win;
xcb_gcontext_t background;
And i added even a XCB_EXPOSE event handler:
case XCB_EXPOSE:{
printf("EXPOSE NEW WINDOW CREATED\n");
xcb_poly_fill_rectangle(connection, win, background,1,&rectangle);
xcb_flush(connection);
}
What is wrong with that code? What am i missing? (I'm trying to develop a very basic window manager, just for fun)
(My idea for that program is that when x is pressed an input box is shown, do you have any suggestion on how to do that?)

how to do automatic image resizing in Python3 with PyGI?

Although I have found partial and indirect answers to this question (see, e.g., this link), I am posting this here because putting together the bits and pieces of the puzzle took me a bit of time, and I thought someone else might find my efforts of use.
So, how to achieve a seamless resizing of images on buttons in GTK+ when the parent window is resized?
The solution offered for PyGTK in the link posted in the question does not work in Python-GI with GTK3, although the trick of using a ScrolledWindow in place of the usual Box was very useful.
Here is my minimal working solution to getting an image on a button to resize with the container.
from gi.repository import Gtk, Gdk, GdkPixbuf
class ButtonWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Button Demo")
self.set_border_width(10)
self.connect("delete-event", Gtk.main_quit)
self.connect("check_resize", self.on_check_resize)
self.box = Gtk.ScrolledWindow()
self.box.set_policy(Gtk.PolicyType.ALWAYS,
Gtk.PolicyType.ALWAYS)
self.add(self.box)
self.click = Gtk.Button()
self.box.add_with_viewport(self.click)
self.pixbuf = GdkPixbuf.Pixbuf().new_from_file('gtk-logo-rgb.jpg')
self.image = Gtk.Image().new_from_pixbuf(self.pixbuf)
self.click.add(self.image)
def resizeImage(self, x, y):
print('Resizing Image to ('+str(x)+','+str(y)+')....')
pixbuf = self.pixbuf.scale_simple(x, y,
GdkPixbuf.InterpType.BILINEAR)
self.image.set_from_pixbuf(pixbuf)
def on_check_resize(self, window):
print("Checking resize....")
boxAllocation = self.box.get_allocation()
self.click.set_allocation(boxAllocation)
self.resizeImage(boxAllocation.width-10,
boxAllocation.height-10)
win = ButtonWindow()
win.show_all()
Gtk.main()
(The -10 on the width and height are to accommodate the inner borders and padding in the button. I tried fiddling with this to get a bigger image on the button, but the result did not look so nice.)
The jpeg file used in this example can be downloaded from here.
I welcome further suggestions on how to do this.
self.image = Gtk.Image().new_from_pixbuf(self.pixbuf)
Should probably be:
self.image = Gtk.Image().set_from_pixbuf(self.pixbuf)
You're creating a new image twice.

How to Get a Window or Fullscreen Screenshot (without PIL)?

With python 3, I'd like to get a handle to another window (not part of my application) such that I can either:
directly capture that window as a screenshot, or
determine its position and size and capture it some other way
In case it is important, I am using Windows XP (edit: works in Windows 7 also).
I found this solution, but it is not quite what I need since it is full screen and more importantly, PIL to the best of my knowledge does not support 3.x yet.
Here's how you can do it using PIL on win32. Given a window handle (hwnd), you should only need the last 4 lines of code. The preceding simply search for a window with "firefox" in the title. Since PIL's source is available, you should be able to poke around the ImageGrab.grab(bbox) method and figure out the win32 code you need to make this happen.
from PIL import ImageGrab
import win32gui
toplist, winlist = [], []
def enum_cb(hwnd, results):
winlist.append((hwnd, win32gui.GetWindowText(hwnd)))
win32gui.EnumWindows(enum_cb, toplist)
firefox = [(hwnd, title) for hwnd, title in winlist if 'firefox' in title.lower()]
# just grab the hwnd for first window matching firefox
firefox = firefox[0]
hwnd = firefox[0]
win32gui.SetForegroundWindow(hwnd)
bbox = win32gui.GetWindowRect(hwnd)
img = ImageGrab.grab(bbox)
img.show()
Ars gave me all the pieces. I am just putting the pieces together here for anyone else who needs to get a screenshot in python 3.x. Next I need to figure out how to work with a win32 bitmap without having PIL to lean on.
Get a Screenshot (pass hwnd for a window instead of full screen):
def screenshot(hwnd = None):
import win32gui
import win32ui
import win32con
from time import sleep
if not hwnd:
hwnd=win32gui.GetDesktopWindow()
l,t,r,b=win32gui.GetWindowRect(hwnd)
h=b-t
w=r-l
hDC = win32gui.GetWindowDC(hwnd)
myDC=win32ui.CreateDCFromHandle(hDC)
newDC=myDC.CreateCompatibleDC()
myBitMap = win32ui.CreateBitmap()
myBitMap.CreateCompatibleBitmap(myDC, w, h)
newDC.SelectObject(myBitMap)
win32gui.SetForegroundWindow(hwnd)
sleep(.2) #lame way to allow screen to draw before taking shot
newDC.BitBlt((0,0),(w, h) , myDC, (0,0), win32con.SRCCOPY)
myBitMap.Paint(newDC)
myBitMap.SaveBitmapFile(newDC,'c:\\tmp.bmp')
Get a Window Handle by title (to pass to the above function):
def _get_windows_bytitle(title_text, exact = False):
def _window_callback(hwnd, all_windows):
all_windows.append((hwnd, win32gui.GetWindowText(hwnd)))
windows = []
win32gui.EnumWindows(_window_callback, windows)
if exact:
return [hwnd for hwnd, title in windows if title_text == title]
else:
return [hwnd for hwnd, title in windows if title_text in title]
This will take a new opened window and make a screenshot of it and then crop it with PIL also possible to find your specific window with pygetwindow.getAllTitles() and then fill in your window name in z3 to get screenshot of only that window.
If you definitely not want to use PIL you can maximize window with pygetwindow module and then make a screenshot with pyautogui module.
Note: not tested on Windows XP (but tested on Windows 10)
import pygetwindow
import time
import os
import pyautogui
import PIL
# get screensize
x,y = pyautogui.size()
print(f"width={x}\theight={y}")
x2,y2 = pyautogui.size()
x2,y2=int(str(x2)),int(str(y2))
print(x2//2)
print(y2//2)
# find new window title
z1 = pygetwindow.getAllTitles()
time.sleep(1)
print(len(z1))
# test with pictures folder
os.startfile("C:\\Users\\yourname\\Pictures")
time.sleep(1)
z2 = pygetwindow.getAllTitles()
print(len(z2))
time.sleep(1)
z3 = [x for x in z2 if x not in z1]
z3 = ''.join(z3)
time.sleep(3)
# also able to edit z3 to specified window-title string like: "Sublime Text (UNREGISTERED)"
my = pygetwindow.getWindowsWithTitle(z3)[0]
# quarter of screen screensize
x3 = x2 // 2
y3 = y2 // 2
my.resizeTo(x3,y3)
# top-left
my.moveTo(0, 0)
time.sleep(3)
my.activate()
time.sleep(1)
# save screenshot
p = pyautogui.screenshot()
p.save(r'C:\\Users\\yourname\\Pictures\\\\p.png')
# edit screenshot
im = PIL.Image.open('C:\\Users\\yourname\\Pictures\\p.png')
im_crop = im.crop((0, 0, x3, y3))
im_crop.save('C:\\Users\\yourname\\Pictures\\p.jpg', quality=100)
# close window
time.sleep(1)
my.close()
The solution here gets a screenshot of a single Window (so can work if the Window is in the background).
Other solutions of this page take picture of the part of the screen the window is on, and thus need to bring the Window to the front first.
Python Screenshot of inactive window PrintWindow + win32gui

Image Misalignment in Visual Studio application

I have a Visual Studio application with a splash screen image cut into "slices". The positions are specified in the Form Designer so they line up properly on the screen. However, the images are out of place when the application is run on the Chinese version of Windows XP. It looks as if the image slices were "exploded" apart.
What's going on here? Do international versions of Windows have a different meaning of the "top left" coordinate of the picture? How can I force the images to be precisely displayed where I want them?
We found a solution! Apparently the picture boxes stretched out on the Chinese XP PC, but the images they contained did not. The fix was to add code like the following:
Me.PictureBoxIcon.Width = Me.PictureBoxIcon.Image.Width
Me.PictureBoxIcon.Height = Me.PictureBoxIcon.Image.Height
Dim loc As New Point
loc.X = Me.PictureBoxIcon.Location.X
loc.Y = Me.PictureBoxIcon.Location.Y + Me.PictureBoxIcon.Height
Me.PictureBoxAbout.Location = loc
Me.PictureBoxAbout.Width = Me.PictureBoxAbout.Image.Width
Me.PictureBoxAbout.Height = Me.PictureBoxAbout.Image.Height
Hope this helps someone else!
In the OnLoad event of the form, you could always explicitly set the location of each section. If starting at the top left with the first and assuming an array with the images in order:
images[0].Location = new Point(0,0);
for (int i = 1; i < images.Length; i++)
{
images[i].Location = new Point(images[i - 1].Location.X + images[i - 1].Width, 0);
}
That will set the first image to the top left corner and all subsequent images to just after the last image.

Resources