Moving PyQt5 App - QPushButton issue - qt

I am working on a simple app for a tea timer with Python3 and PyQt5 on OSX. For aesthetic reasons I decided to remove the window frame. In order to still keep the window moveable I overloaded the mousePressEvent() and mouseMoveEvent() handlers.
However I run into a problem when the window is being moved while the mouse is over one of the buttons (QPushButton()). The cursor then jumps to the last click position and the window is moved from there. Does anyone have an idea as to why this is happening? Interestingly the same issue does not appear when clicking and moving on a QLabel() object...
If you want to try it out, here is an example code:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
## ===============================================================
## IMPORTS
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
## ===============================================================
## CONSTANTS
WINDOW_WIDTH = 690
WINDOW_HEIGHT = 435
## ===============================================================
## CLASSES
class Form(QWidget):
def __init__(self, parent=None):
super().__init__()
self.windowPos = QPoint() # Maybe not necessary when button click and window movement are seperated
# Declare and specify UI elements
self.timerLabel = QLabel("00:00") # Might have to change data type here
self.timerLabel.setObjectName("timerLabel")
self.infoLabel = QLabel("No tea selected")
self.infoLabel.setObjectName("infoLabel")
self.teaOneButton = QPushButton("Tea One")
self.teaOneButton.setObjectName("teaOneButton")
self.teaTwoButton = QPushButton("Tea Two")
self.teaTwoButton.setObjectName("teaTwoButton")
# Arrange UI elements in a layout
grid = QGridLayout()
self.setLayout(grid) # Set the QGridLayout as the window's main layout
grid.setSpacing(0) # Spacing between widgets - does not work if window is resized
grid.setContentsMargins(4, 4, 4, 4)
grid.addWidget(self.timerLabel, 0, 0, 1, -1, Qt.AlignHCenter) # http://doc.qt.io/qt-5/qgridlayout.html#addWidget
grid.addWidget(self.infoLabel, 1, 0, 1, -1, Qt.AlignHCenter)
grid.addWidget(self.teaOneButton, 2, 0)
grid.addWidget(self.teaTwoButton, 2, 1)
self.resize(WINDOW_WIDTH, WINDOW_HEIGHT)
# Arranging window in center of the screen by overloading showEvent method
def showEvent(self, QShowEvent):
self.centerOnScreen()
def centerOnScreen(self):
screen = QDesktopWidget()
screenGeom = QRect(screen.screenGeometry(self))
screenCenterX = screenGeom.center().x()
screenCenterY = screenGeom.center().y()
self.move(screenCenterX - self.width() / 2,
screenCenterY - self.height() / 2)
# Overload mouseEvent handlers to make window moveable
def mousePressEvent(self, QMouseEvent):
self.windowPos = QMouseEvent.pos()
self.setCursor(QCursor(Qt.SizeAllCursor))
def mouseReleaseEvent(self, QMouseEvent):
self.setCursor(QCursor(Qt.ArrowCursor))
def mouseMoveEvent(self, QMouseEvent):
# print (self.childAt(QMouseEvent.pos()) == QLabel)
pos = QPoint(QMouseEvent.globalPos())
self.window().move(pos - self.windowPos)
## ===============================================================
## MAIN LOOP
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
screen = Form()
screen.setWindowFlags(Qt.FramelessWindowHint)
screen.show()
sys.exit(app.exec_())

Well guys I think I might have found a way to circumvent the problem.
It seemed to me, that the problem might be that QPushButtons handle mouseMoveEvents differently than, for example QLabel objects. To me it makes sense intuitively since a button object might need different mouse event handling than other unclickable objects.
So what I did was try to implement a subclass for QPushButton, overloading the mouseMoveEvent handler. At first I thought I might set the handler to ignore the event in order to delegate it up to the parent widget. That did not change anything. It actually might be something that has been going on all along. Because when I implement some sort of code into the event handling function, the button does no longer function as a source for moving the window. See for yourself:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
## ===============================================================
## IMPORTS
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
## ===============================================================
## CONSTANTS
WINDOW_WIDTH = 690
WINDOW_HEIGHT = 435
## ===============================================================
## CLASSES
class UnmoveableButton(QPushButton):
def __init__(self, text=""):
super().__init__(text)
# This event function is overloaded in order to avoid the widget from delegating the event up to the parent.
# This way, the pre-existing functionality is skipped, i.e. the window can no longer be moved while hovering over a button.
def mouseMoveEvent(self, QMouseEvent):
pass
class Form(QWidget):
def __init__(self, parent=None):
super().__init__()
self.windowPos = QPoint() # Maybe not necessary when button click and window movement are seperated
# Declare and specify UI elements
self.timerLabel = QLabel("00:00") # Might have to change data type here
self.timerLabel.setObjectName("timerLabel")
self.infoLabel = QLabel("No tea selected")
self.infoLabel.setObjectName("infoLabel")
self.teaOneButton = UnmoveableButton("Tea One")
self.teaOneButton.setObjectName("teaOneButton")
self.teaTwoButton = UnmoveableButton("Tea Two")
self.teaTwoButton.setObjectName("teaTwoButton")
# Arrange UI elements in a layout
grid = QGridLayout()
self.setLayout(grid) # Set the QGridLayout as the window's main layout
grid.setSpacing(0) # Spacing between widgets - does not work if window is resized
grid.setContentsMargins(4, 4, 4, 4)
grid.addWidget(self.timerLabel, 0, 0, 1, -1, Qt.AlignHCenter) # http://doc.qt.io/qt-5/qgridlayout.html#addWidget
grid.addWidget(self.infoLabel, 1, 0, 1, -1, Qt.AlignHCenter)
grid.addWidget(self.teaOneButton, 2, 0)
grid.addWidget(self.teaTwoButton, 2, 1)
self.resize(WINDOW_WIDTH, WINDOW_HEIGHT)
# Arranging window in center of the screen by overloading showEvent method
def showEvent(self, QShowEvent):
self.centerOnScreen()
def centerOnScreen(self):
screen = QDesktopWidget()
screenGeom = QRect(screen.screenGeometry(self))
screenCenterX = screenGeom.center().x()
screenCenterY = screenGeom.center().y()
self.move(screenCenterX - self.width() / 2,
screenCenterY - self.height() / 2)
# Overload mouseEvent handlers to make window moveable
def mousePressEvent(self, QMouseEvent):
self.windowPos = QMouseEvent.pos()
self.setCursor(QCursor(Qt.SizeAllCursor))
def mouseReleaseEvent(self, QMouseEvent):
self.setCursor(QCursor(Qt.ArrowCursor))
def mouseMoveEvent(self, QMouseEvent):
# print (self.childAt(QMouseEvent.pos()) == QLabel)
pos = QPoint(QMouseEvent.globalPos())
self.window().move(pos - self.windowPos)
## ===============================================================
## MAIN LOOP
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
screen = Form()
screen.setWindowFlags(Qt.FramelessWindowHint)
screen.show()
sys.exit(app.exec_())
I think it might not be the most elegant or correct solution for that matter, but it works fine for me right now. If someone has an idea on how to improve upon this, let me know :)

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
Are you sure there is no namespace clash from imported members?

Related

How do I prevent double valueChanged events when I press the arrows in a QSpinbox?

Could be a either a Qt bug, or a bug in my app, I am hoping someone can help me diagnose this.
I am building a PyQt5 application with an interface built using qtdesigner and pyuic5.
I have to QSpinBox widgets in my window, which I connect to two methods like this:
self.img1IndexBox.valueChanged.connect(self.changeImage1)
self.img2IndexBox.valueChanged.connect(self.changeImage2)
Everything seems to work fine in the application, except for one thing: If I scroll the mouse over the spinbox, I can make it increment the value. If I change the value with text, all works fine. If I use keyboard arrows on the spinbox, it works fine. But if I click on either the up or down arrows from the spinbox, I get get two changeValue events, a double increment. Like I clicked twice. In fact, it even looks from the animation that it is creating an event for the downpress, and another when the button goes back up.
Could this be just a library bug, or what could be causing this in my program? How could I debug this?
You might be able to prevent that double thing by setting spinbox enable to false.
then make it enable to true after processing large data.
Upon clicking the arrow up/down
on_valuechanged
ui->spinbox->setEnabled(false);
then set to true before the function on_valuechanged ends.
ui->spinbox->setEnabled(true);
Apparently the problem is the event is triggering a very long routine, this delays the "button release" event, and it is enough time to make the system think the user is actually holding the button, generating more events... But I would still be interested in learning what would be a good walk-around. Would there be a nice pyqt-onic way to start a thread for that method?
http://www.qtcentre.org/archive/index.php/t-43078.html
Instead of using valueChanged.connect use editingFinished.connect it will make sure the function is called only after value is provided.
PyQt
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDoubleSpinBox, QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSpinBox
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
# def valueChanged(self, value):
# print(value)
def valueChanged(self):
print(f"Value changed new value is : {self.spinBox.value()}")
def initUI(self):
self.setGeometry(0, 0, 200, 100)
self.layout = QVBoxLayout()
self.spinBox = QSpinBox()
self.spinBox.setAlignment(Qt.AlignCenter)
self.spinBox.setRange(0, 1000)
# self.spinBox.valueChanged.connect(self.valueChanged)
self.spinBox.editingFinished.connect(self.valueChanged)
self.layout.addWidget(self.spinBox)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec())
PySide
import sys
from PySide6.QtWidgets import QDoubleSpinBox, QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSpinBox
from PySide6.QtGui import Qt
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
# def valueChanged(self, value):
# print(value)
def valueChanged(self):
print(f"Value changed new value is : {self.spinBox.value()}")
def initUI(self):
self.setGeometry(0, 0, 200, 100)
self.layout = QVBoxLayout()
self.spinBox = QSpinBox()
self.spinBox.setAlignment(Qt.AlignCenter)
self.spinBox.setRange(0, 1000)
# self.spinBox.valueChanged.connect(self.valueChanged)
self.spinBox.editingFinished.connect(self.valueChanged)
self.layout.addWidget(self.spinBox)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec())

Resize QDialog when RadioButton is Checked PyQt

not so many experience at all with pyqt..
I have designed an UI with Qt Designer with 2 Radiobuttons.
Depending on the RadioButton selected some widgets are visible and other not. What I'm not trying to do is to resize automatically the layout of the dialog depending on how many widgets are visible in the UI.
The question is very similar to this one . Here the extract of the code that might be helpful to understand the problem (I know it is just a piece of the code, but the entire dialog is pretty long):
class MyDialog(QDialog, FORM_CLASS):
..........
# connect the radioButton with a function that reloads the UI
self.radioSingle.toggled.connect(self.refreshWidgets)
.....
# dictionary with all the widgets and the values
self.widgetType = {
self.cmbModelName: ['all'],
self.cmbGridLayer: ['all'],
self.cmbRiverLayer: ['all'],
self.lineNewLayerEdit: ['all'],
self.lineLayerNumber: ['single']
}
# function that refresh the UI with the correct widgets depending on the radiobutton selected
def refreshWidgets(self, idx):
'''
refresh UI widgets depending on the radiobutton chosen
'''
tp = self.radioSingle.isChecked()
for k, v in self.widgetType.items():
if tp:
if 'all' or 'single' in v:
active = True
k.setEnabled(active)
k.setVisible(active)
else:
active = 'all' in v
k.setEnabled(active)
k.setVisible(active)
# attempt to resize the Dialog
self.setSizeConstraint()
Surely the code could be improved (not so skilled with code writing)...
You just gotta set_up your UI to resize when it's needed.
There is a lot of more beautiful way to do this, but here is a simple and fast example:
You just have to define your layout to behave the way you want. If you make use of Minimum and Maximum size it'll resize as you need.
You could also have your own personalized buttons, widgets, .... you would have a cleaner code.
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QRadioButton
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QWidget
class CustomDialog(QDialog):
w_container = None
v_layout_container = None
v_scroll_area = None
v_layout_preview = None
def __init__(self):
"""Init UI."""
super(CustomDialog, self).__init__()
self.init_ui()
def init_ui(self):
"""Init all ui object requirements."""
self.r1 = QRadioButton("smaller")
self.r2 = QRadioButton("bigger")
style = "background: green;"
size = [200, 50]
self.small_size = [200, 250]
self.big_size = [200, 500]
self.resize(200,200)
self.w1 = QWidget()
self.w2 = QWidget()
self.w3 = QWidget()
self.w4 = QWidget()
self.w5 = QWidget()
self.w6 = QWidget()
self.w1.setStyleSheet(style)
self.w2.setStyleSheet(style)
self.w3.setStyleSheet(style)
self.w4.setStyleSheet(style)
self.w5.setStyleSheet(style)
self.w6.setStyleSheet(style)
self.w1.setFixedSize(size[0], size[1])
self.w2.setFixedSize(size[0], size[1])
self.w3.setFixedSize(size[0], size[1])
self.w4.setFixedSize(size[0], size[1])
self.w5.setFixedSize(size[0], size[1])
self.w6.setFixedSize(size[0], size[1])
self.full_container = QVBoxLayout()
self.full_container.setContentsMargins(0,0,0,0)
self.radio_widget = QWidget()
self.radio_widget.setFixedSize(200, 50)
self.radio_container = QHBoxLayout()
self.radio_container.setContentsMargins(0,0,0,0)
self.widget_widget = QWidget()
self.widget_container = QVBoxLayout()
self.radio_widget.setLayout(self.radio_container)
self.widget_widget.setLayout(self.widget_container)
self.full_container.addWidget(self.radio_widget)
self.full_container.addWidget(self.widget_widget)
self.radio_container.addWidget(self.r1)
self.radio_container.addWidget(self.r2)
self.widget_container.addWidget(self.w1)
self.widget_container.addWidget(self.w2)
self.widget_container.addWidget(self.w3)
self.widget_container.addWidget(self.w4)
self.widget_container.addWidget(self.w5)
self.widget_container.addWidget(self.w6)
self.setLayout(self.full_container)
self.r1.setChecked(True)
def paintEvent(self, event):
if self.r1.isChecked():
self.w4.hide()
self.w5.hide()
self.w6.hide()
self.setFixedSize(self.small_size[0],self.small_size[1])
elif self.r2.isChecked():
self.w4.show()
self.w5.show()
self.w6.show()
self.setFixedSize(self.big_size[0], self.big_size[1])
self.full_container.update()
super(CustomDialog, self).paintEvent(event)
def run():
app = QApplication(sys.argv)
GUI = CustomDialog()
GUI.show()
sys.exit(app.exec_())
run()
Observe that I'm using PyQt5, so if you need you just gotta change your imports to the version you are using.

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))

What class should I use to create painting application by Qt?

It is second time to post this question. Because first one didn't show any effort.
I want to create simple painting application which consist with just black/white pen and canvas. So to create my painting app I want to know what class should I use. Or is there painting soft which is open source and created by Qt?
I know there is QPainter class. So using QPainter is the right way? I heard it is low level graphical tool. So is there more useful one? And I think following source code is one of the implementation of painter app. But Is this right way? I think there is more good way.
My code:
https://gist.github.com/keimina/469fa17508ae2c0c90c4#file-simplepaintapp-py
from PySide.QtGui import QApplication, QMainWindow, QAction, QActionGroup, QWidget, QCursor, QPainter
from PySide.QtCore import QTimer
import PySide.QtCore as QtCore
class W(QWidget):
def __init__(self):
QWidget.__init__(self)
self.resize(400,400)
self.myIsMousePressing = False
self.p = QPainter(self)
self.autoFillBackground()
self.x = 0
self.y = 0
self.r = dict()#{(x,Y,49, 49):rect}
self.penColor = 1
def mousePressEvent(self, event):
self.myIsMousePressing = True
def mouseReleaseEvent(self, event):
self.myIsMousePressing = False
def myTimeOut(self):
if self.myIsMousePressing:
pos = self.mapFromGlobal(QCursor.pos())
self.x = pos.x()/50
self.y = pos.y()/50
self.r[(self.x*50, self.y*50, 49, 49)] = self.penColor
def paintEvent(self, event):
self.p.begin(self)
for k in self.r.keys():
if self.r[k] == 1:
self.p.setPen(QtCore.Qt.black)
self.p.setBrush(QtCore.Qt.black)
else:
self.p.setPen(QtCore.Qt.white)
self.p.setBrush(QtCore.Qt.white)
self.p.drawRect(*k)
self.p.end()
self.update()
class MyWidget(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(400, 400)
self.initMenu()
self.w = W()
self.setCentralWidget(self.w)
self.t = QTimer(self.w)
self.t.timeout.connect(self.w.myTimeOut)
self.t.start(1)
def initMenu(self):
self.fileMenu = self.menuBar().addMenu("&File")
self.editMenu = self.menuBar().addMenu("&Edit")
self.helpMenu = self.menuBar().addMenu("&Help")
self.fileMenuAction = QAction("&New", self)
self.editMenuAction1 = QAction("&Black", self)
self.editMenuAction2 = QAction("&White", self)
self.helpMenuAction = QAction("&About", self)
actGroup = QActionGroup(self)
actGroup.addAction(self.editMenuAction1)
actGroup.addAction(self.editMenuAction2)
self.editMenuAction1.setCheckable(True)
self.editMenuAction2.setCheckable(True)
self.editMenuAction1.setChecked(True)
self.fileMenu.addAction(self.fileMenuAction)
self.editMenu.addAction(self.editMenuAction1)
self.editMenu.addAction(self.editMenuAction2)
self.helpMenu.addAction(self.helpMenuAction)
self.editMenuAction1.triggered.connect(self.action1)
self.editMenuAction2.triggered.connect(self.action2)
def action1(self):
self.w.penColor = 1
def action2(self):
self.w.penColor = 2
app = QApplication([])
mainWin = MyWidget()
mainWin.show()
app.exec_()
Thanks.
P.S. I'm using PySide but any other Qt is OK.
QPainter is essentially the only way, short of manipulating individual pixels in a QImage, or using OpenGL, to paint something in Qt. So its use goes without saying and if you paint in Qt, you will have to use QPainter. That's how painting is done. But this has nothing to do with an application that a human might use to "paint". The painting we're talking about is what your application has to do to show something to the user, no matter what the user is doing.
What you're asking is if there's something application-specific in Qt that would help with implementing a "drawing" application. If you're after a vector drawing application, then the graphics scene framework might be of use. Otherwise, there's nothing to help you. You'll have to start with a plain QWidget and implement the behavior you need.

How to show clickable QFrame without loosing focus from main window?

Finally I am able to create a chrome like tab in Qt/PyQt QMainWindow. After unsuccessfully trying to port this Qt C++ non client area painting code, I revise my thinking to be this way : trick visually by displaying a free floating QFrame that get resized and moved together with main window. Surely this is not a perfect solution (for example this code still don't solve when to disable topmost hint where the another application is on top of the main application window, but I think that's quite easy to solve)
What I want to ask in this page, is how to keep the click action on this QFrame window button from stealing focus from main window? At the moment I simply reactivate the main window when click action does occur. But it creates flashing effect on the mainwindow titlebar. I believe this SO page gives the answer, but I haven't been able to create a successful result from this C++ code:
HWND winHandle = (HWND)winId();
ShowWindow(winHandle, SW_HIDE);
SetWindowLong(winHandle, GWL_EXSTYLE, GetWindowLong(winHandle, GWL_EXSTYLE)
| WS_EX_NOACTIVATE | WS_EX_APPWINDOW);
ShowWindow(winHandle, SW_SHOW);
Into this PyQt code:
def no_focus(self):
import ctypes, win32con, win32gui
dc = win32gui.GetWindowDC(self.winId())
user32 = ctypes.windll.user32
user32.SetWindowLongW(dc, win32con.GWL_EXSTYLE, user32.GetWindowLongW(dc, win32con.GWL_EXSTYLE) | win32con.WS_EX_NOACTIVATE | win32con.WS_EX_APPWINDOW)
Would love to let you see and test the fully functional code below:
__author__ = 'Eko Wibowo'
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class FSTabHeader(QFrame):
def __init__(self, parent):
super(FSTabHeader, self).__init__(None)
self.mainwindow = parent
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.SplashScreen)
self.setFocusPolicy(Qt.NoFocus)
self.setAttribute(Qt.WA_ShowWithoutActivating)
layout = QHBoxLayout(self)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
tab_text = 'Click me'
self.btn_tab = QPushButton(self)
self.btn_tab.setStyleSheet('border:1px')
self.btn_tab.setContentsMargins(0,0,0,0)
self.btn_tab.setText(tab_text)
self.btn_tab.setMinimumHeight(25 + 1)
self.btn_tab.setMaximumHeight(25 + 1)
self.btn_tab.setMinimumWidth(60)
self.btn_tab.setMaximumWidth(60)
self.btn_tab.setCursor(Qt.PointingHandCursor)
self.btn_tab.clicked.connect(self.dummy)
layout.addWidget(self.btn_tab)
self.setLayout(layout)
self.show()
def dummy(self):
print 'it create flashes effect on mainwindow titlebar'
self.mainwindow.activateWindow()
def no_focus(self):
import ctypes, win32con, win32gui
dc = win32gui.GetWindowDC(self.winId())
user32 = ctypes.windll.user32
user32.SetWindowLongW(dc, win32con.GWL_EXSTYLE, user32.GetWindowLongW(dc, win32con.GWL_EXSTYLE) | win32con.WS_EX_NOACTIVATE | win32con.WS_EX_APPWINDOW)
def adjust_position(self):
top_left = self.mainwindow.mapToGlobal(self.mainwindow.rect().topLeft())
self.move(top_left.x() + 20 + 5, top_left.y() - self.height() + 1)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.tab_header = FSTabHeader(self)
self.tab_header.no_focus()
def resizeEvent(self, *args, **kwargs):
self.tab_header.adjust_position()
def moveEvent(self, *args, **kwargs):
self.tab_header.adjust_position()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainWindow = MainWindow(None)
mainWindow.show()
app.exec_()
Any suggestions?

Resources