Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

autoscroll qt symbol and improvments #6

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This simple Python script gives you a Windows-like autoscroll feature on Linux.

1. Clone the repository:
```
git clone https://github.com/TWolczanski/linux-autoscroll.git
git clone https://github.com/adeliktas/linux-autoscroll.git
```
2. Create a Python virtual environment and activate it:
```
Expand All @@ -13,7 +13,8 @@ source .autoscroll/bin/activate
```
3. Install pynput:
```
python3 -m pip install pynput
pip3 install --user pynput
(python3 -m pip install pynput)
```
4. Add the following shebang to the script (substitute `/path/to` with the actual path to your virtual environment):
```
Expand Down Expand Up @@ -52,3 +53,8 @@ By default the scrolling begins when the mouse pointer is 30 px below or above t
Click the middle mouse button (or the button you assigned to `BUTTON_START`) and move your mouse to start scrolling. The further you move the mouse (vertically) from the point where you have clicked the button, the faster the scrolling becomes. To leave the scroll mode, simply press the middle mouse button again (or press the button you assigned to `BUTTON_STOP`).

Note that at slow speed the scrolling is not smooth and (probably) there is no way to make it smooth. However, one can easily get used to it.

## Todo

-improve scroll smoothness
-fix losing window title bar control handle
183 changes: 132 additions & 51 deletions autoscroll.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,135 @@
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from pynput.mouse import Button, Controller, Listener
from threading import Event
from time import sleep

def on_move(x, y):
global pos, scroll_mode, direction, interval, DELAY, DEAD_AREA
if scroll_mode.is_set():
delta = pos[1] - y
if abs(delta) <= DEAD_AREA:
direction = 0
elif delta > 0:
direction = 1
elif delta < 0:
direction = -1
if abs(delta) <= DEAD_AREA + DELAY * 2:
interval = 0.5
else:
interval = DELAY / (abs(delta) - DEAD_AREA)

def on_click(x, y, button, pressed):
global pos, scroll_mode, direction, interval, BUTTON_START, BUTTON_STOP
if button == BUTTON_START and pressed and not scroll_mode.is_set():
pos = (x, y)
direction = 0
interval = 0
scroll_mode.set()
elif button == BUTTON_STOP and pressed and scroll_mode.is_set():
scroll_mode.clear()
import time

xpos,ypos = Controller().position
crosssizepx=30

from PyQt5.QtCore import Qt, QObject, pyqtSignal, QTimer

from tkinter import Tk
import subprocess

class QMouseListener(QObject):
mouse_moved = pyqtSignal(int, int)
mouse_clicked = pyqtSignal(int, int, Button, bool)

def __init__(self, parent=None):
super().__init__(parent=parent)
self.listener = Listener(on_move=self.mouse_moved.emit, on_click=self.mouse_clicked.emit)

def start(self):
self.listener.start()

class Autoscrollsymbol(QtWidgets.QWidget):
def __init__(self, parent=None, windowSize=0, penWidth=0):
QtWidgets.QWidget.__init__(self, parent)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.WindowTransparentForInput | QtCore.Qt.Tool)
self.setStyleSheet("background:transparent")
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.setGeometry(xpos, ypos, 50, 50)
self.pen = QtGui.QPen(QtGui.QColor(0,255,0,255))
self.pen.setWidth(penWidth)
self.mouse_listener = QMouseListener(self)
self.mouse_listener.mouse_moved.connect(self.on_move)
self.mouse_listener.mouse_clicked.connect(self.on_click)
self.mouse_listener.start()
self.scroll_mode = 0
self.mouse = Controller()
self.pos = self.mouse.position
self.direction = 0

# modify this to adjust the speed of scrolling
self.DELAY = 3000
# modify this to change the button used for using scroll mode
self.BUTTON_Scroll = Button.middle
# modify this to change the size (in px) of the area below and above the starting point where the scrolling is paused
self.DEAD_AREA = 20

#Trigger Delay 250ms to avoid instant changes
self.Triggerdelay = 0.25
self.Timestart = 0
self.Timeend = 0
self.Timedelta = 0

self.autoscroll()

def on_move(self, x, y):
if self.scroll_mode:
delta = self.pos[1] - y
absdelta = abs(delta)
if absdelta <= self.DEAD_AREA:
self.direction = 0
elif delta > 0:
self.direction = 1
elif delta < 0:
self.direction = -1
if absdelta <= self.DEAD_AREA:
self.timer.setInterval(500)
else:
self.timer.setInterval(int(self.DELAY / (absdelta - self.DEAD_AREA)))
if not self.scroll_mode:
self.move(x -int(crosssizepx/2),y -int(crosssizepx/2))

def clearclip(self):
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append('')
r.update()
r.destroy()
subprocess.run(["xsel","-c"])

def on_click(self, x, y, button, pressed):
if button == self.BUTTON_Scroll and pressed and not self.scroll_mode:
self.setWindowOpacity(1.0)
self.pos = (x, y)
self.direction = 0
self.timer.setInterval(100)
self.scroll_mode = 1
self.timer.start()
self.Timestart = time.time()
elif button == self.BUTTON_Scroll and not pressed and self.scroll_mode:
self.Timeend = time.time()
self.Timedelta = self.Timeend - self.Timestart
if self.Timedelta>self.Triggerdelay:
self.scroll_mode = 0
self.timer.stop()
self.setWindowOpacity(0.0)
elif (button == self.BUTTON_Scroll or button==Button.left) and pressed and self.scroll_mode:
self.scroll_mode = 0
self.timer.stop()
self.setWindowOpacity(0.0)

def autoscroll():
global mouse, scroll_mode, direction, interval
while True:
scroll_mode.wait()
sleep(interval)
mouse.scroll(0, direction)

mouse = Controller()
listener = Listener(on_move = on_move, on_click = on_click)
scroll_mode = Event()
pos = mouse.position
direction = 0
interval = 0

# modify this to adjust the speed of scrolling
DELAY = 5
# modify this to change the button used for entering the scroll mode
BUTTON_START = Button.middle
# modify this to change the button used for exiting the scroll mode
BUTTON_STOP = Button.middle
# modify this to change the size (in px) of the area below and above the starting point where the scrolling is paused
DEAD_AREA = 30

listener.start()
autoscroll()
if (button == Button.middle) and pressed:
self.clearclip()

def paintEvent(self, event):
painter = QtGui.QPainter(self)
pen = QtGui.QPen(QtCore.Qt.green, 2, QtCore.Qt.SolidLine)
painter.setPen(pen)
#painter.setPen(self.pen)
painter.drawEllipse(0, 0, (crosssizepx), (crosssizepx))
painter.drawLine(0, int(crosssizepx/2), crosssizepx, int(crosssizepx/2))#-
painter.drawLine(int(crosssizepx/2), 0, int(crosssizepx/2), crosssizepx)#|
self.setWindowOpacity(0.0)

def scrolldown(self):
self.mouse.scroll(0, self.direction)

def autoscroll(self):
self.timer = QTimer(self)
self.timer.setInterval(100)
self.timer.timeout.connect(self.scrolldown)
self.timer.start()

def mouselistener(self):
self.listener=Listener(on_move=self.on_move)
self.listener.start()

app = QtWidgets.QApplication(sys.argv)
widget = Autoscrollsymbol(windowSize=1, penWidth=10)
widget.show()
sys.exit(app.exec_())