Skip to content

Commit

Permalink
New indication and other changes (#4)
Browse files Browse the repository at this point in the history
* Change the way to display tomatoes count

* Add reset action

* Close about and settings by "ESC", not main window

* Prepare version

* Change organization name

* Fix bug when settings were not applied

* Update readme
  • Loading branch information
yakimka authored Jan 29, 2020
1 parent 6f5baf3 commit 8b10bb2
Show file tree
Hide file tree
Showing 14 changed files with 123 additions and 30 deletions.
4 changes: 2 additions & 2 deletions CherryTomato/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
MEDIA_DIR = os.path.join(BASE_DIR, 'media')
APP_ICON = os.path.join(MEDIA_DIR, 'icon.png')

VERSION = '0.3.0'
ORGANIZATION_NAME = 'CherryTomato'
VERSION = '0.4.0'
ORGANIZATION_NAME = 'yakimka'
APPLICATION_NAME = 'CherryTomato'
7 changes: 7 additions & 0 deletions CherryTomato/about_window.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from PyQt5 import Qt
from PyQt5 import QtWidgets
from PyQt5.QtGui import QIcon

Expand All @@ -15,3 +16,9 @@ def __init__(self):
self.labelTitle.setText(title)

self.setWindowIcon(QIcon(APP_ICON))

def keyPressEvent(self, event):
if event.key() == Qt.Qt.Key_Escape:
self.close()
else:
super().keyPressEvent(event)
2 changes: 1 addition & 1 deletion CherryTomato/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from CherryTomato import ORGANIZATION_NAME, APPLICATION_NAME
from CherryTomato.main_window import CherryTomatoMainWindow

QCoreApplication.setApplicationName(ORGANIZATION_NAME)
QCoreApplication.setOrganizationName(ORGANIZATION_NAME)
QCoreApplication.setApplicationName(APPLICATION_NAME)

app = Qt.QApplication(sys.argv)
Expand Down
4 changes: 4 additions & 0 deletions CherryTomato/main_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ def setupUi(self, MainWindow):
self.actionAbout.setObjectName("actionAbout")
self.actionSettings = QtWidgets.QAction(MainWindow)
self.actionSettings.setObjectName("actionSettings")
self.actionReset = QtWidgets.QAction(MainWindow)
self.actionReset.setObjectName("actionReset")
self.menuFile.addAction(self.actionReset)
self.menuFile.addAction(self.actionSettings)
self.menuFile.addAction(self.actionAbout)
self.menuBar.addAction(self.menuFile.menuAction())
Expand All @@ -68,4 +71,5 @@ def retranslateUi(self, MainWindow):
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.actionAbout.setText(_translate("MainWindow", "About"))
self.actionSettings.setText(_translate("MainWindow", "Settings"))
self.actionReset.setText(_translate("MainWindow", "Reset"))
from CherryTomato.widget import QRoundProgressBar, QRoundPushbutton
6 changes: 6 additions & 0 deletions CherryTomato/main_ui.ui
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ border-radius: 20px;</string>
<property name="title">
<string>File</string>
</property>
<addaction name="actionReset"/>
<addaction name="actionSettings"/>
<addaction name="actionAbout"/>
</widget>
Expand All @@ -114,6 +115,11 @@ border-radius: 20px;</string>
<string>Settings</string>
</property>
</action>
<action name="actionReset">
<property name="text">
<string>Reset</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
Expand Down
19 changes: 8 additions & 11 deletions CherryTomato/main_window.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os

from PyQt5 import Qt, QtCore
from PyQt5.QtGui import QBrush, QColor, QPalette, QIcon
from PyQt5.QtGui import QBrush, QColor, QPalette, QIcon, QKeySequence
from PyQt5.QtMultimedia import QSound

from CherryTomato import about_window, APP_ICON, MEDIA_DIR, settings_window
Expand Down Expand Up @@ -35,6 +35,9 @@ def __init__(self, parent=None):
self.actionSettings.triggered.connect(self.showSettingsWindow)
self.settingsWindow.closing.connect(self.update)

self.actionReset.setShortcut(QKeySequence('Ctrl+R'))
self.actionReset.triggered.connect(self.tomatoTimer.reset)

def update(self):
self.tomatoTimer.updateState()

Expand Down Expand Up @@ -80,10 +83,10 @@ def closeEvent(self, e):

@Qt.pyqtSlot()
def display(self):
TOMATO_SIGN = '˙'
TOMATO_SIGN = '🍅'
minutes, seconds = int(self.tomatoTimer.seconds // 60), int(self.tomatoTimer.seconds % 60)
display = f'\n{minutes:02d}:{seconds:02d}\n{TOMATO_SIGN * self.tomatoTimer.tomatoes}'
self.progress.setFormat(display)
self.progress.setFormat(f'{minutes:02d}:{seconds:02d}')
self.progress.setSecondFormat(f'{TOMATO_SIGN}x{self.tomatoTimer.tomatoes}')
self.progress.setValue(self.tomatoTimer.progress)
self.changeButtonState()

Expand All @@ -94,7 +97,7 @@ def display(self):

@Qt.pyqtSlot()
def do_start(self):
# trigger through proxy
# TODO trigger through proxy
if not self.tomatoTimer.running:
self.tomatoTimer.start()
else:
Expand Down Expand Up @@ -137,12 +140,6 @@ def setGreen(self):
green = (60, 187, 111)
self.setColor(lightGreen, green)

def keyPressEvent(self, event):
if event.key() == Qt.Qt.Key_Escape:
self.close()
else:
super().keyPressEvent(event)

@Qt.pyqtSlot()
def setFocusOnWindowAndPlayNotification(self):
if self.settings.interrupt:
Expand Down
8 changes: 7 additions & 1 deletion CherryTomato/settings_window.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from PyQt5 import QtWidgets
from PyQt5 import QtWidgets, Qt
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QIcon

Expand Down Expand Up @@ -32,6 +32,12 @@ def __init__(self):
self.autoStopBreak.setChecked(self.settings.autoStopBreak)
self.switchToTomatoOnAbort.setChecked(self.settings.switchToTomatoOnAbort)

def keyPressEvent(self, event):
if event.key() == Qt.Qt.Key_Escape:
self.close()
else:
super().keyPressEvent(event)

def closeEvent(self, e):
e.accept()
self.updateSettings()
Expand Down
18 changes: 12 additions & 6 deletions CherryTomato/tomato_timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

from CherryTomato.settings import CherryTomatoSettings

settings = CherryTomatoSettings()


class TomatoTimer(QObject):
TICK_TIME = 64 # ms
Expand All @@ -15,7 +13,7 @@ class TomatoTimer(QObject):
def __init__(self):
super().__init__()

self.settings = settings
self.settings = CherryTomatoSettings()

self.tomatoes = 0
self.state = None
Expand Down Expand Up @@ -47,7 +45,7 @@ def abort(self):
if not self.isTomato() and self.settings.switchToTomatoOnAbort:
self.changeState()
else:
self.reset()
self.resetTime()
self.notifyAboutAnyChange()

def createTimer(self):
Expand Down Expand Up @@ -100,14 +98,22 @@ def _isTimeForLongBreak(self):

def updateState(self):
if not self.running:
self.reset()
self.resetTime()
self.notifyAboutAnyChange()

def reset(self):
def resetTime(self):
self.seconds = self.state.time

def notifyAboutAnyChange(self):
self.stateChanged.emit()

def notifyTimerIsOver(self):
self.finished.emit()

def reset(self):
self.stop()
self.tomatoes = 0
self.state = None
del self._states
self.changeState()
self.notifyAboutAnyChange()
73 changes: 70 additions & 3 deletions CherryTomato/widget.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import os

from PyQt5 import QtCore
from PyQt5.QtCore import QRectF, Qt
from PyQt5.QtGui import QPainter, QFont, QFontMetricsF, QPaintEvent, QImage
from PyQt5.QtWidgets import QPushButton
from qroundprogressbar import QRoundProgressBar as QRoundProgressBar_

from CherryTomato import MEDIA_DIR
from qroundprogressbar import QRoundProgressBar as QRoundProgressBar_


class QRoundPushbutton(QPushButton):
Expand Down Expand Up @@ -39,11 +41,76 @@ def applyStyleSheet(self):
class QRoundProgressBar(QRoundProgressBar_):
def __init__(self, parent=None):
super().__init__(parent)
self.m_second_format = ''

self.setFormat('')
self.setValue(0)
self.setBarStyle(QRoundProgressBar.BarStyle.LINE)
self.setOutlinePenWidth(6)
self.setDataPenWidth(6)
self.setOutlinePenWidth(4)
self.setDataPenWidth(4)

def resizeEvent(self, e):
self.setMaximumWidth(self.height())

def setSecondFormat(self, val: str):
if val != self.m_second_format:
self.m_second_format = val
self.valueFormatChanged()

def paintEvent(self, event: QPaintEvent):
outerRadius = min(self.width(), self.height())
baseRect = QRectF(1, 1, outerRadius - 2, outerRadius - 2)
buffer = QImage(outerRadius, outerRadius, QImage.Format_ARGB32_Premultiplied)
p = QPainter(buffer)
p.setRenderHint(QPainter.Antialiasing)
self.rebuildDataBrushIfNeeded()
self.drawBackground(p, buffer.rect())
self.drawBase(p, baseRect)
if self.m_value > 0:
delta = (self.m_max - self.m_min) / (self.m_value - self.m_min)
else:
delta = 0
self.drawValue(p, baseRect, self.m_value, delta)
firstInnerRect, secondInnerRect, innerRadius = self.calculateInnerRect(outerRadius)
self.drawInnerBackground(p, firstInnerRect)
self.drawInnerBackground(p, secondInnerRect)
self.drawTwoTexts(p, firstInnerRect, secondInnerRect, innerRadius, self.m_value)
p.end()
painter = QPainter(self)
painter.fillRect(baseRect, self.palette().window())
painter.drawImage(0, 0, buffer)

def calculateInnerRect(self, outerRadius: float):
innerRect, innerRadius = super().calculateInnerRect(outerRadius)

left, top, width, height = innerRect.getRect()

firstHeight = height * 0.7
firstInnerRect = QRectF(left, top, width, firstHeight)
secondInnerRect = QRectF(left, top + firstHeight, width, height - firstHeight)
return firstInnerRect, secondInnerRect, innerRadius

def drawTwoTexts(self,
p: QPainter,
firstRect: QRectF,
secondRect: QRectF,
innerRadius: float,
value: float):
if not self.m_format:
return
f = QFont(self.font())
f.setPixelSize(10)
fm = QFontMetricsF(f)
maxWidth = fm.width(self.valueToText(self.m_max))
delta = innerRadius / maxWidth
timeFontSize = f.pixelSize() * delta * 0.75
f.setPixelSize(int(timeFontSize))
p.setFont(f)
timeTextRect = QRectF(firstRect)
tomatoesTextRect = QRectF(secondRect)
p.setPen(self.palette().text().color())
p.drawText(timeTextRect, Qt.AlignCenter | Qt.AlignBottom, self.valueToText(value))
tomatoesFontSize = timeFontSize * 0.3
f.setPixelSize(int(tomatoesFontSize))
p.setFont(f)
p.drawText(tomatoesTextRect, Qt.AlignHCenter, self.m_second_format)
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CherryTomato

![Screenshot](assets/Screenshot_20191211_004536.png)
![Screenshot](assets/screenshot.png)

## Features

Expand Down
Binary file removed assets/Screenshot_20191211_004536.png
Binary file not shown.
Binary file added assets/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(name='CherryTomato',
author='yakimka',
version='0.1.0',
version='0.4.0',
packages=['CherryTomato'],
install_requires=['PyQt5', 'qroundprogressbar'],
url='https://github.com/yakimka/CherryTomato',
Expand Down
8 changes: 4 additions & 4 deletions tests/test_tomato_timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def test_stop(mock_qt_timer, tomato):

@pytest.fixture
def mock_change_reset(mocker):
return mocker.patch('CherryTomato.tomato_timer.TomatoTimer.reset')
return mocker.patch('CherryTomato.tomato_timer.TomatoTimer.resetTime')


@pytest.fixture
Expand All @@ -76,7 +76,7 @@ def test_abort(mock_for_abort, tomato):
tomato.abort()

tomato.stop.assert_called_once()
tomato.reset.assert_called_once()
tomato.resetTime.assert_called_once()
assert tomato.isTomato()


Expand Down Expand Up @@ -136,9 +136,9 @@ def test_change_state_to_long_break(tomato, tomatoes, const_value, expected):
assert tomato.seconds == expected.time


def test_reset(tomato):
def test_reset_time(tomato):
tomato.seconds = 1

tomato.reset()
tomato.resetTime()

assert tomato.seconds == 100

0 comments on commit 8b10bb2

Please sign in to comment.