Paint on Transparent window with HeaderBar Python and Gtk3 - python

I have a little problem with the following code. It creates a GtkWindow, make it paintable so I can draw on it with cairo on draw events. Then I add a GtkHeaderBar and a simple button widget.
from gi.repository import Gtk
import cairo
def draw_callback(widget,cr):
if widget.transparency:
cr.set_source_rgba(0,0,0,0.5)
else:
cr.set_source_rgb(0,0,0)
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.paint()
cr.set_operator(cairo.OPERATOR_OVER)
win= Gtk.Window()
win.connect('delete-event', Gtk.main_quit)
win.set_app_paintable(True)
screen = win.get_screen()
visual = screen.get_rgba_visual()
win.transparency = False
if visual and screen.is_composited():
win.set_visual(visual)
win.transparency = True
else:
print('System doesn\'t support transparency')
win.set_visual(screen.get_system_visual)
win.connect('draw', draw_callback)
win.add(Gtk.Button(label='test'))
bar = Gtk.HeaderBar(title='title')
bar.set_has_subtitle(False)
bar.set_show_close_button(True)
win.set_titlebar(bar)
win.show_all()
Gtk.main()
The draw_callback paint the window background but this background looks like this :
It is like the cairo context doesn't have the same size of the window. The part that is draw and that is outside the window doesn't seems to answer to mouse event ( I cannot grab the window from this part for example)
If I don't use an HeaderBar, I don't have this problem.
This works with ruby 2.2 and the Gtk3 bindings.
My python Version is python 3 and the up to date Gtk modules
Edit:
Problem still exist with :
def size_allocation_cb(widget, rectangle):
widget.x = rectangle.x
widget.y = rectangle.y
widget.width = rectangle.width
widget.height = rectangle.height
win.connect('size-allocate', size_allocation_cb)
def draw_callback(widget,cr):
if widget.transparency:
cr.set_source_rgba(0,0,0,0.5)
else:
cr.set_source_rgb(0,0,0)
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.rectangle(widget.x, widget.y, widget.width, widget.height)
cr.fill()
cr.set_operator(cairo.OPERATOR_OVER)

When you have a header bar, your window is drawn entirely on the client side, including shadows (for some reason). You will need to call cairo_rectangle() or some other function, preferably with the size allocation (not size request!) of the child of the GtkWindow, to clip cairo_paint() to the correct size.

Related

How to Pygtk WindowTypeHint to DOCK on XFCE and have it visible on the desktop

So I've run into an issue where whenever I set the WindowTypeHint to anything other than normal the window just disappears. I've verified the hint type with print.Below is my sample code
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
# Create the main window
class MyWindow(Gtk.Window):
def __init__(self):
super().__init__(title="Hello World")
# Innitially setting the bar to off
self.set_decorated(False)
# Attempting to set window type to prevent minimizing when show desktop is hit and to stay behind other objects etc.
self.set_type_hint(Gdk.WindowTypeHint.DOCK)
# Defining the button and drawing it
self.button = Gtk.Button(label="Click Here")
self.button.connect("clicked", self.on_button_clicked)
self.add(self.button)
# define the button functions
def on_button_clicked(self, widget):
if win.props.decorated == False:
win.set_decorated(True)
else:
win.set_decorated(False)
print("Hello World")
print(win.props.decorated)
print(self.props.type_hint)
# Set alias for the window
win = MyWindow()
# Testing. Print the hint type to console
print(win.props.type_hint)
# Window settings.
win.set_keep_below(True)
win.connect("destroy", Gtk.main_quit)
win.show_all()
print(win.props.type_hint)
Gtk.main()
Window to appear as a dock on the desktop where the mouse pointer is or in the corner of one of the monitors the same way glava works with it's settings.
Item was drawing off screen. Multiple monitors had it throwing the item into the corner off screen between them. If anyone else comes across this I resolved it by setting the coordinates with self.move(x, y) in the initialisation. Found where I wanted it to root with win.get_position() on button press to find where I wanted it.

Vedo Plotter in Qt - Mouse Click Event not registering at the correct position

I have tried several different things about the mouse clicks not registering correctly in my Qt mainwindow. When using only my QHD monitor, the program worked just fine (video). However, when using my laptop (zoomed in at 1792 x 1120) as the only display, the mouse clicks seemed to have a varying up-right offset and register more accurately near the bottom left corner of the widget (video). I am suspicious that the screen resolution of the display might cause a problem for vedo.
The mouse event is a vedo plotter event. Changing the "screensize", "size", "pos" attributes of the plotter did not fix the issue.
I looked up some examples provided by vedo, specifically mousehover.py and qt_window1.py. The mousehover example worked fine on my laptop. However, adding a clicking event in qt_window1.py also created the same issue. Therefore, the problem most likely was caused by the qt widget.
def __init__(self,size):
super(MainWindow, self).__init__()
# load the components defined in th xml file
loadUi("viewer_gui.ui", self)
self.screenSize = size
# Connections for all elements in Mainwindow
self.pushButton_inputfile.clicked.connect(self.getFilePath)
self.pushButton_clearSelection.clicked.connect(self.clearScreen)
self.action_selectVertex.toggled.connect(self.actionSelection_state_changed)
self.action_selectActor.toggled.connect(self.actionSelection_state_changed)
# Set up VTK widget
self.vtkWidget = QVTKRenderWindowInteractor()
self.splitter_viewer.addWidget(self.vtkWidget)
# ipy console
self.ipyConsole = QIPythonWidget(customBanner="Welcome to the embedded ipython console\n")
self.splitter_viewer.addWidget(self.ipyConsole)
self.ipyConsole.pushVariables({"foo":43, "print_process_id":print_process_id, "ipy":self.ipyConsole, "self":self})
self.ipyConsole.printText("The variable 'foo' and the method 'print_process_id()' are available.\
Use the 'whos' command for information.\n\nTo push variables run this before starting the UI:\
\n ipyConsole.pushVariables({\"foo\":43,\"print_process_id\":print_process_id})")
# Create renderer and add the vedo objects and callbacks
self.plt = Plotter(qtWidget=self.vtkWidget,bg='DarkSlateBlue',bg2='MidnightBlue',screensize=(1792,1120))
self.id1 = self.plt.addCallback("mouse click", self.onMouseClick)
self.id2 = self.plt.addCallback("key press", self.onKeypress)
self.plt.show() # <--- show the vedo rendering
def onMouseClick(self, event):
if(self.action_selectActor.isChecked()):
self.selectActor(event)
elif(self.action_selectVertex.isChecked()):
self.selectVertex(event)
def selectActor(self,event):
if(not event.actor):
return
printc("You have clicked your mouse button. Event info:\n", event, c='y')
printc("Left button pressed on", [event.picked3d])
# adding a silhouette might cause some lags
# self.plt += event.actor.silhouette().lineWidth(2).c('red')
#an alternative solution
self.actorSelection = event.actor.clone()
self.actorSelection.c('red')
self.plt += self.actorSelection
def selectVertex(self,event):
if(not event.isPoints):
return
# print(arr[event.actor.closestPoint(event.picked3d, returnPointId=True)])
printc("You have clicked your mouse button. Event info:\n", event, c='y')
printc("Left button pressed on 3d: ", [event.picked3d])
printc("Left button pressed on 2d: ", [event.picked2d])
p = pointcloud.Point(pos=(event.picked3d[0],event.picked3d[1],event.picked3d[2]),r=12,c='red',alpha=0.5)
self.vertexSelections.append(p)
self.plt += p
Running the following lines:
app = QApplication(sys.argv)
screen = app.primaryScreen()
print('Screen: %s' % screen.name())
size = screen.size()
print('Size: %d x %d' % (size.width(), size.height()))
outputted:
Screen: Color LCD
Size: 1792 x 1120
This turns out to be an upstream bug in vtk, unrelated to vedo. After downgrading VTK to version 8.1.2 and Python to 3.7, the clicking issue disappeared when running the program on my laptop. Other people also encountered the same problem, and here is an error report on vtk’s website that describes how vtkPropPicker is now returning the wrong world coordinates.

How to disable title bar transparency for my own application in Windows 7 with Python Tkinter

How to disable title bar transparency for my own application (to create a solid title bar) in Windows 7 with Python Tkinter?
I'm not talking about changing the theme of Windows 7. I'm asking if I'm using the default windows 7 theme, which show title bar with some level of transparency, allowing to see a fuzzy image of the background. Now I'm creating a Python GUI application with Python Tkinter, for this particular application I don't want such transparency. I want a totally solid title bar. What shall I do in my Python code? I know that there are some applications which are able to do that.
The attached image show two applications. The above one is an application I installed on my computer. It has a solid title bar (I didn't change the theme) which is what I want. The second one is the application I created with Python Tkinter with transparent title bar under the same theme, which behaves similar to most of the applications, but is not what I want .
It's impossible with tkinter alone.
Sure, tkinter provides some options like an icon, a title, a child-toplevel, a tool window, etc, but don't be fooled by these options!
While he has an interface to communicate with window manager, he's mainly a tool for the client area of the window, and window managment/decoration isn't his purpose.
Operating with the Aero stuff is more complicated thing, than a trivial window styling, and it handled by the DWM, which can be controlled via the DWM composition API(1, 2).
For all that interaction we need the ctypes library (comes with python) and a set of the WinAPI functions:
DwmIsCompositionEnabled - to check if the Aero is enabled (system-wide).
DwmSetWindowAttribute - to specify (enable/disable) the non-client area rendering policy (and therefore revert to the Basic/Aero frame).
GetParent - to get a hwnd of the owner window, because we're trying to make changes in the non-client area.
Try to run this piece of code:
import ctypes as ct
import tkinter as tk
class App(tk.Tk):
is_composition_enabled = ct.windll.dwmapi.DwmIsCompositionEnabled
set_window_attribute = ct.windll.dwmapi.DwmSetWindowAttribute
get_parent = ct.windll.user32.GetParent
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.state = True
self.minsize(width=250, height=100)
self.frame = tk.Frame(self)
self.frame.pack(expand=True, fill='both')
self.status_label = tk.Label(self.frame)
self.status_label.pack(expand=True, fill='both')
self.populate_status_label()
self.toggle_button = tk.Button(self.frame, text='Toggle glass', command=self.toggle_glass)
self.toggle_button.pack(fill='x')
def populate_status_label(self):
status = 'en' if self.state else 'dis'
self.status_label.configure(text='Glass border %sabled!' % status)
def toggle_glass(self):
response = self.handle_glass()
if response:
self.state = not self.state
self.populate_status_label()
else:
print('Something bad happened!')
def handle_glass(self):
try:
composition_enabled = ct.c_bool()
response = not bool(self.is_composition_enabled(ct.byref(composition_enabled)))
if response and composition_enabled.value:
hwnd = self.get_parent(self.winfo_id())
switch_to_disable, switch_to_enable, rendering_policy = 1, 2, 2
dwma_ncrendering_switch = ct.c_int(switch_to_disable if self.state else switch_to_enable)
return not bool(self.set_window_attribute(hwnd, rendering_policy, ct.byref(dwma_ncrendering_switch),
ct.sizeof(dwma_ncrendering_switch)))
except AttributeError:
return False
app = App()
app.mainloop()
As a result, you should get something similar to this:
However, if your goal is to only disable a border transparency, while maintaining a same aero-like style, there's not much you can do about it.

How to get a ms windows desktop height in pygtk?

I wrote application in pygtk, that display a popup (like from Chrome) window, without resizing and moving option. All is great except one thing. I have to move this window to bottom of the screen, little above the taskbar.
Taskbar on MS windows has, on windows XP 30px, but on windows 7 is higher
I have the monitor / screen resolution getting by code:
w = self.get_screen()
print w.get_height()
but i still don't have a height of taskbar. Any ideas how to get this height?
On Windows you can use this:
from ctypes import windll, wintypes, byref
SPI_GETWORKAREA = 48
SM_CYSCREEN = 1
def get_taskbar_size():
SystemParametersInfo = windll.user32.SystemParametersInfoA
work_area = wintypes.RECT()
if (SystemParametersInfo(SPI_GETWORKAREA, 0, byref(work_area), 0)):
GetSystemMetrics = windll.user32.GetSystemMetrics
return GetSystemMetrics(SM_CYSCREEN) - work_area.bottom
print get_taskbar_size() # 30
Note that get_taskbar_size will return None if the API call failed.

hide window from MS windows taskbar

Using pyGtk I created a window without decoration. The Window is hidden from task bar and top of all windows. On linux it works fine, but on MS Windows window sometimes it hides under some other window and always has "python.exe" the taskbar in windows.
Image representing my problem:
How can I hide this "python.exe" window from taskbar?
My code:
class Infowindow(gtk.Window):
'''
Klasa okienka informacyjnego
'''
def __init__(self, json, index, destroy_cb, device):
gtk.Window.__init__(self)
self.size_x = 260+48
self.size_y = 85
self.separator_size = 10
self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_SPLASHSCREEN)
self.set_decorated(False)
self.set_property('skip-taskbar-hint', True)
self.set_opacity(1)
self.set_keep_above(True)
self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
self.connect("enter-notify-event", self.__on_hover)
self.connect("leave-notify-event", self.__on_leave)
self.connect("button_press_event", self.__on_click)
self.set_size_request(self.size_x, self.size_y)
color = gtk.gdk.color_parse('#f3f3f3')
self.modify_bg(gtk.STATE_NORMAL, color)
self.expanded = False
self.index = index
self.destroy_cb = destroy_cb
self.json = json['data']
self.system_info = False if 'system' not in self.json or not self.json['system'] else True
self.device = device
f = gtk.Frame()
self.move_window(index) #move window to specified place
self.box_area = gtk.VBox()
self.box_area.set_spacing(10)
f.add(self.box_area)
self.add(f)
self.show_all()
Again thanks David Heffernan. Works perfect!
For people who want a full solution in python.
Name your windowin a characteristic way for example: 'alamakota'
Use find_window('alamakota'),
With given handler use hide_from_taskbar(handler)
Last use set_topmost(handler)
Window is hidden from taskbar and it's alwoays on top.
I know it's not a beatyfull code, but works fine on windows XP and above.
import ctypes
import win32gui
import win32api
from win32con import SWP_NOMOVE
from win32con import SWP_NOSIZE
from win32con import SW_HIDE
from win32con import SW_SHOW
from win32con import HWND_TOPMOST
from win32con import GWL_EXSTYLE
from win32con import WS_EX_TOOLWINDOW
#staticmethod
def find_window(name):
try:
return win32gui.FindWindow(None, name)
except win32gui.error:
print("Error while finding the window")
return None
#staticmethod
def hide_from_taskbar(hw):
try:
win32gui.ShowWindow(hw, SW_HIDE)
win32gui.SetWindowLong(hw, GWL_EXSTYLE,win32gui.GetWindowLong(hw, GWL_EXSTYLE)| WS_EX_TOOLWINDOW);
win32gui.ShowWindow(hw, SW_SHOW);
except win32gui.error:
print("Error while hiding the window")
return None
#staticmethod
def set_topmost(hw):
try:
win32gui.SetWindowPos(hw, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE)
except win32gui.error:
print("Error while move window on top")
You have two options to remove a window from the taskbar:
Add the WS_EX_TOOLWINDOW extended window style. But this has other consequences and I cannot say whether or not they are serious.
Give the window an owner that is not visible. This allows you freedom to use whatever window styles and extended styles you wish, but does involve more work.
It's natural that your window will go beneath other windows. That's how windows works. If you want to make your window appear on top, show it with HWND_TOPMOST.
I've no idea how any of this is (or is not) implemented under PyGtk. I've just given you the Win32 answer!
Win32 solution provided in the other answer is not very easy and it does not play well with the GtkWindow::show method. A simple solution now in Gtk3 is:
win->set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_UTILITY); //This works
win->set_skip_taskbar_hint(); //This does not guarantee to work

Categories