
Versiyon-1 :
Kod: Tümünü seç
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# =============================================================================
# Project : VB6 -> Python Conversion (FINAL PROJECT)
# Module : Home Internet Cafe Timer — Mini AkınSoft
# Description : Tracks internet session time and calculates billing cost.
# - Start : records start time, begins live elapsed counter
# - Stop : records end time, calculates duration + total cost
# - Settings menu : set hourly rate (validated min/max bounds)
# - Live ticker : duration + running cost updates every second
# - Cost formula : (elapsed_seconds / 3600) * hourly_rate
# Platform : GNU/Linux Mint 22.2 MATE x64 | Windows 11 24H2 x64
# Python : 3.8+ (stdlib only: tkinter, datetime)
# =============================================================================
import os
import sys
# ---------------------------------------------------------------------------
# VENV BOOTSTRAPPER (v1.1 - robust)
# ---------------------------------------------------------------------------
VENV_DIR = "/home/linuxmaster/python-toolchain"
_SENTINEL_FLAG = "_VENV_BOOTSTRAP_DONE"
_candidates = [
os.path.join(VENV_DIR, "bin", "python3"),
os.path.join(VENV_DIR, "bin", "python"),
os.path.join(VENV_DIR, "Scripts", "python.exe"),
]
def _venv_is_healthy() -> bool:
lib = os.path.join(VENV_DIR, "lib")
if os.path.isdir(lib):
for e in os.listdir(lib):
if os.path.isdir(os.path.join(lib, e, "site-packages")):
return True
return os.path.isdir(os.path.join(VENV_DIR, "Lib", "site-packages"))
def _bootstrap_venv() -> None:
if os.environ.get(_SENTINEL_FLAG) == "1":
return
inside = os.path.normcase(os.path.realpath(sys.executable)).startswith(
os.path.normcase(os.path.realpath(VENV_DIR)))
if inside or not _venv_is_healthy():
return
for c in _candidates:
if os.path.isfile(c):
os.environ[_SENTINEL_FLAG] = "1"
os.execv(c, [c] + sys.argv)
_bootstrap_venv()
# ---------------------------------------------------------------------------
# IMPORTS
# ---------------------------------------------------------------------------
import tkinter as tk
from tkinter import messagebox, simpledialog
from datetime import datetime
# ---------------------------------------------------------------------------
# CONSTANTS
# ---------------------------------------------------------------------------
WINDOW_TITLE = "Internet Timer"
TICK_MS = 1000
DEFAULT_RATE = 200_000
MIN_RATE = 1
MAX_RATE = 99_999_999
CURRENCY = "TL"
# ---------------------------------------------------------------------------
# HELPERS
# ---------------------------------------------------------------------------
def format_duration(seconds: int) -> str:
h = seconds // 3600
m = (seconds % 3600) // 60
s = seconds % 60
return f"{h} hour {m} minute {s} second"
def format_money(amount: float) -> str:
return f"{int(amount):,} {CURRENCY}".replace(",", ".")
def calc_cost(seconds: int, hourly_rate: int) -> float:
return (seconds / 3600.0) * hourly_rate
# ---------------------------------------------------------------------------
# MAIN APPLICATION
# ---------------------------------------------------------------------------
class InternetTimerApp:
def __init__(self, master: tk.Tk) -> None:
self.master = master
self._running = False
self._start_dt = None
self._after_id = None
self._hourly_rate = DEFAULT_RATE
master.title(WINDOW_TITLE)
master.resizable(False, False)
self._build_menu()
self._build_ui()
self._center_window()
master.protocol("WM_DELETE_WINDOW", self._on_quit)
self._refresh_rate_display()
# ------------------------------------------------------------------
# MENU
# ------------------------------------------------------------------
def _build_menu(self) -> None:
menubar = tk.Menu(self.master)
settings = tk.Menu(menubar, tearoff=0)
settings.add_command(label="Set Hourly Rate ...", command=self._op_set_rate)
settings.add_separator()
settings.add_command(label="Quit", command=self._on_quit)
menubar.add_cascade(label="Settings", menu=settings)
self.master.config(menu=menubar)
# ------------------------------------------------------------------
# UI
# ------------------------------------------------------------------
def _build_ui(self) -> None:
FL = ("Helvetica", 10, "bold")
FE = ("Courier", 12)
FR = ("Courier", 13, "bold")
FB = ("Helvetica", 11, "bold")
outer = tk.Frame(self.master, padx=14, pady=12)
outer.pack()
# ---- TOP SECTION ----
top = tk.Frame(outer)
top.pack(fill=tk.X, pady=(0, 10))
# Left — buttons
bf = tk.Frame(top, padx=6, pady=6, relief=tk.GROOVE, bd=2)
bf.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 16))
self.btn_start = tk.Button(
bf, text="Start", width=10, font=FB,
bg="#d4edda", activebackground="#a8d5b5",
relief=tk.RAISED, bd=3, command=self._op_start)
self.btn_start.pack(pady=(6, 8))
self.btn_stop = tk.Button(
bf, text="Stop", width=10, font=FB,
bg="#f8d7da", activebackground="#e8a0a8",
relief=tk.RAISED, bd=3, state=tk.DISABLED,
command=self._op_stop)
self.btn_stop.pack(pady=(0, 6))
# Right — time fields
rf = tk.Frame(top)
rf.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
for lbl_text, var_attr, bg in [
("Start time:", "var_start", "#ffffff"),
("End time:", "var_end", "#ffffff"),
("Duration:", "var_duration", "#fffbe6"),
]:
tk.Label(rf, text=lbl_text, font=FL, anchor="w").pack(fill=tk.X)
var = tk.StringVar(value="")
setattr(self, var_attr, var)
tk.Entry(rf, textvariable=var, font=FE, state="readonly",
readonlybackground=bg, width=28,
relief=tk.SUNKEN).pack(fill=tk.X, pady=(0, 8))
# ---- SEPARATOR ----
tk.Frame(outer, height=2, bg="#aaaaaa").pack(fill=tk.X, pady=8)
# ---- BOTTOM SECTION ----
bottom = tk.Frame(outer)
bottom.pack(fill=tk.X)
for lbl_text, var_attr, bg in [
("Hourly rate:", "var_rate", "#eaf4fb"),
("Total amount:", "var_total", "#fff0e6"),
]:
row = tk.Frame(bottom)
row.pack(fill=tk.X, pady=(0, 5))
tk.Label(row, text=lbl_text, font=FL,
width=14, anchor="w").pack(side=tk.LEFT)
var = tk.StringVar(value="")
setattr(self, var_attr, var)
tk.Entry(row, textvariable=var, font=FR, state="readonly",
readonlybackground=bg, width=20,
relief=tk.SUNKEN).pack(side=tk.LEFT)
# ---- STATUS BAR ----
self.lbl_status = tk.Label(
self.master,
text=" Ready — press Start to begin session.",
anchor="w", relief=tk.SUNKEN,
font=("Courier", 9), fg="#444444", bg="#e8e8e8",
)
self.lbl_status.pack(fill=tk.X, side=tk.BOTTOM)
# ------------------------------------------------------------------
# SETTINGS
# ------------------------------------------------------------------
def _op_set_rate(self) -> None:
raw = simpledialog.askstring(
"Set Hourly Rate",
f"Enter hourly rate ({MIN_RATE:,} – {MAX_RATE:,} {CURRENCY}):\n"
f"Current rate: {self._hourly_rate:,} {CURRENCY}",
initialvalue=str(self._hourly_rate),
parent=self.master,
)
if raw is None:
return
try:
value = int(raw.strip().replace(".", "").replace(",", ""))
except ValueError:
messagebox.showwarning("Invalid Rate",
"Please enter a whole number.", parent=self.master)
return
if not (MIN_RATE <= value <= MAX_RATE):
messagebox.showwarning(
"Out of Range",
f"Hourly rate must be between "
f"{MIN_RATE:,} and {MAX_RATE:,} {CURRENCY}.",
parent=self.master,
)
return
self._hourly_rate = value
self._refresh_rate_display()
self._set_status(f"Hourly rate updated: {value:,} {CURRENCY}")
def _refresh_rate_display(self) -> None:
self.var_rate.set(format_money(self._hourly_rate))
# ------------------------------------------------------------------
# START / STOP
# ------------------------------------------------------------------
def _op_start(self) -> None:
if self._running:
return
self._start_dt = datetime.now()
self._running = True
self.var_start.set(self._start_dt.strftime("%H:%M:%S"))
self.var_end.set("")
self.var_duration.set("")
self.var_total.set("")
self.btn_start.config(state=tk.DISABLED)
self.btn_stop.config(state=tk.NORMAL)
self._set_status("Session running ...")
self._tick()
def _op_stop(self) -> None:
if not self._running:
return
if self._after_id is not None:
self.master.after_cancel(self._after_id)
self._after_id = None
self._running = False
end_dt = datetime.now()
elapsed_sec = int((end_dt - self._start_dt).total_seconds())
cost = calc_cost(elapsed_sec, self._hourly_rate)
self.var_end.set(end_dt.strftime("%H:%M:%S"))
self.var_duration.set(format_duration(elapsed_sec))
self.var_total.set(format_money(cost))
self.btn_start.config(state=tk.NORMAL)
self.btn_stop.config(state=tk.DISABLED)
self._set_status(
f"Session ended. Duration: {format_duration(elapsed_sec)}"
f" | Total: {format_money(cost)}"
)
# ------------------------------------------------------------------
# LIVE TICKER
# ------------------------------------------------------------------
def _tick(self) -> None:
if not self._running:
return
elapsed_sec = int((datetime.now() - self._start_dt).total_seconds())
self.var_duration.set(format_duration(elapsed_sec))
self.var_total.set(format_money(calc_cost(elapsed_sec, self._hourly_rate)))
self._after_id = self.master.after(TICK_MS, self._tick)
# ------------------------------------------------------------------
# MISC
# ------------------------------------------------------------------
def _set_status(self, text: str) -> None:
self.lbl_status.config(text=f" {text}")
def _on_quit(self) -> None:
if self._running:
if not messagebox.askyesno(
"Session Active",
"A session is currently running.\n\n"
"Quit without stopping the session?",
icon=messagebox.WARNING, parent=self.master,
):
return
if self._after_id is not None:
self.master.after_cancel(self._after_id)
self.master.destroy()
def _center_window(self) -> None:
self.master.update_idletasks()
w = self.master.winfo_width()
h = self.master.winfo_height()
sw = self.master.winfo_screenwidth()
sh = self.master.winfo_screenheight()
self.master.geometry(f"+{(sw - w) // 2}+{(sh - h) // 2}")
# ---------------------------------------------------------------------------
# ENTRY POINT
# ---------------------------------------------------------------------------
def main() -> None:
root = tk.Tk()
InternetTimerApp(root)
root.mainloop()
input("\nPress ENTER to exit.")
if __name__ == "__main__":
main()
Kod: Tümünü seç
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# =============================================================================
# Project : VB6 -> Python Conversion (FINAL PROJECT)
# Module : Home Internet Cafe Timer — Mini AkınSoft
# Description : Tracks internet session time and calculates billing cost.
# - Start : records start time, begins live elapsed counter
# - Stop : records end time, calculates duration + total cost
# - Settings menu : set hourly rate (validated min/max bounds)
# - Live ticker : duration + running cost updates every second
# - Cost formula : (elapsed_seconds / 3600) * hourly_rate
# Platform : GNU/Linux Mint 22.2 MATE x64 | Windows 11 24H2 x64
# Python : 3.8+ (stdlib only: tkinter, datetime)
# =============================================================================
import os
import sys
# ---------------------------------------------------------------------------
# VENV BOOTSTRAPPER (v1.1 - robust)
# ---------------------------------------------------------------------------
VENV_DIR = "/home/linuxmaster/python-toolchain"
_SENTINEL_FLAG = "_VENV_BOOTSTRAP_DONE"
_candidates = [
os.path.join(VENV_DIR, "bin", "python3"),
os.path.join(VENV_DIR, "bin", "python"),
os.path.join(VENV_DIR, "Scripts", "python.exe"),
]
def _venv_is_healthy() -> bool:
lib = os.path.join(VENV_DIR, "lib")
if os.path.isdir(lib):
for e in os.listdir(lib):
if os.path.isdir(os.path.join(lib, e, "site-packages")):
return True
return os.path.isdir(os.path.join(VENV_DIR, "Lib", "site-packages"))
def _bootstrap_venv() -> None:
if os.environ.get(_SENTINEL_FLAG) == "1":
return
inside = os.path.normcase(os.path.realpath(sys.executable)).startswith(
os.path.normcase(os.path.realpath(VENV_DIR)))
if inside or not _venv_is_healthy():
return
for c in _candidates:
if os.path.isfile(c):
os.environ[_SENTINEL_FLAG] = "1"
os.execv(c, [c] + sys.argv)
_bootstrap_venv()
# ---------------------------------------------------------------------------
# IMPORTS
# ---------------------------------------------------------------------------
import tkinter as tk
from tkinter import messagebox, simpledialog
from datetime import datetime
import socket
# ---------------------------------------------------------------------------
# CONSTANTS
# ---------------------------------------------------------------------------
WINDOW_TITLE = "Internet Timer"
TICK_MS = 1000
NET_CHECK_MS = 3000 # network poll interval (3 seconds)
NET_HOST = "8.8.8.8" # Google DNS — reliable, lightweight
NET_PORT = 53
NET_TIMEOUT = 2 # seconds
DEFAULT_RATE = 200_000
MIN_RATE = 1
MAX_RATE = 99_999_999
CURRENCY = "TL"
# ---------------------------------------------------------------------------
# HELPERS
# ---------------------------------------------------------------------------
def is_connected() -> bool:
"""
Return True if the machine can reach the internet.
Uses a raw TCP connect to Google DNS (8.8.8.8:53).
No DNS lookup is performed — result is purely connectivity-based.
Works identically on GNU/Linux and Windows.
"""
try:
socket.setdefaulttimeout(NET_TIMEOUT)
with socket.create_connection((NET_HOST, NET_PORT)):
return True
except OSError:
return False
def format_duration(seconds: int) -> str:
h = seconds // 3600
m = (seconds % 3600) // 60
s = seconds % 60
return f"{h} hour {m} minute {s} second"
def format_money(amount: float) -> str:
return f"{int(amount):,} {CURRENCY}".replace(",", ".")
def calc_cost(seconds: int, hourly_rate: int) -> float:
return (seconds / 3600.0) * hourly_rate
# ---------------------------------------------------------------------------
# MAIN APPLICATION
# ---------------------------------------------------------------------------
class InternetTimerApp:
def __init__(self, master: tk.Tk) -> None:
self.master = master
self._running = False
self._start_dt = None
self._after_id = None
self._net_id = None # network monitor after() handle
self._hourly_rate = DEFAULT_RATE
master.title(WINDOW_TITLE)
master.resizable(False, False)
self._build_menu()
self._build_ui()
self._center_window()
master.protocol("WM_DELETE_WINDOW", self._on_quit)
self._refresh_rate_display()
# ------------------------------------------------------------------
# MENU
# ------------------------------------------------------------------
def _build_menu(self) -> None:
menubar = tk.Menu(self.master)
settings = tk.Menu(menubar, tearoff=0)
settings.add_command(label="Set Hourly Rate ...", command=self._op_set_rate)
settings.add_separator()
settings.add_command(label="Quit", command=self._on_quit)
menubar.add_cascade(label="Settings", menu=settings)
self.master.config(menu=menubar)
# ------------------------------------------------------------------
# UI
# ------------------------------------------------------------------
def _build_ui(self) -> None:
FL = ("Helvetica", 10, "bold")
FE = ("Courier", 12)
FR = ("Courier", 13, "bold")
FB = ("Helvetica", 11, "bold")
outer = tk.Frame(self.master, padx=14, pady=12)
outer.pack()
# ---- TOP SECTION ----
top = tk.Frame(outer)
top.pack(fill=tk.X, pady=(0, 10))
# Left — buttons
bf = tk.Frame(top, padx=6, pady=6, relief=tk.GROOVE, bd=2)
bf.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 16))
self.btn_start = tk.Button(
bf, text="Start", width=10, font=FB,
bg="#d4edda", activebackground="#a8d5b5",
relief=tk.RAISED, bd=3, command=self._op_start)
self.btn_start.pack(pady=(6, 8))
self.btn_stop = tk.Button(
bf, text="Stop", width=10, font=FB,
bg="#f8d7da", activebackground="#e8a0a8",
relief=tk.RAISED, bd=3, state=tk.DISABLED,
command=self._op_stop)
self.btn_stop.pack(pady=(0, 6))
# Right — time fields
rf = tk.Frame(top)
rf.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
for lbl_text, var_attr, bg in [
("Start time:", "var_start", "#ffffff"),
("End time:", "var_end", "#ffffff"),
("Duration:", "var_duration", "#fffbe6"),
]:
tk.Label(rf, text=lbl_text, font=FL, anchor="w").pack(fill=tk.X)
var = tk.StringVar(value="")
setattr(self, var_attr, var)
tk.Entry(rf, textvariable=var, font=FE, state="readonly",
readonlybackground=bg, width=28,
relief=tk.SUNKEN).pack(fill=tk.X, pady=(0, 8))
# ---- SEPARATOR ----
tk.Frame(outer, height=2, bg="#aaaaaa").pack(fill=tk.X, pady=8)
# ---- BOTTOM SECTION ----
bottom = tk.Frame(outer)
bottom.pack(fill=tk.X)
for lbl_text, var_attr, bg in [
("Hourly rate:", "var_rate", "#eaf4fb"),
("Total amount:", "var_total", "#fff0e6"),
]:
row = tk.Frame(bottom)
row.pack(fill=tk.X, pady=(0, 5))
tk.Label(row, text=lbl_text, font=FL,
width=14, anchor="w").pack(side=tk.LEFT)
var = tk.StringVar(value="")
setattr(self, var_attr, var)
tk.Entry(row, textvariable=var, font=FR, state="readonly",
readonlybackground=bg, width=20,
relief=tk.SUNKEN).pack(side=tk.LEFT)
# ---- STATUS BAR ----
self.lbl_status = tk.Label(
self.master,
text=" Ready — press Start to begin session.",
anchor="w", relief=tk.SUNKEN,
font=("Courier", 9), fg="#444444", bg="#e8e8e8",
)
self.lbl_status.pack(fill=tk.X, side=tk.BOTTOM)
# ------------------------------------------------------------------
# SETTINGS
# ------------------------------------------------------------------
def _op_set_rate(self) -> None:
raw = simpledialog.askstring(
"Set Hourly Rate",
f"Enter hourly rate ({MIN_RATE:,} – {MAX_RATE:,} {CURRENCY}):\n"
f"Current rate: {self._hourly_rate:,} {CURRENCY}",
initialvalue=str(self._hourly_rate),
parent=self.master,
)
if raw is None:
return
try:
value = int(raw.strip().replace(".", "").replace(",", ""))
except ValueError:
messagebox.showwarning("Invalid Rate",
"Please enter a whole number.", parent=self.master)
return
if not (MIN_RATE <= value <= MAX_RATE):
messagebox.showwarning(
"Out of Range",
f"Hourly rate must be between "
f"{MIN_RATE:,} and {MAX_RATE:,} {CURRENCY}.",
parent=self.master,
)
return
self._hourly_rate = value
self._refresh_rate_display()
self._set_status(f"Hourly rate updated: {value:,} {CURRENCY}")
def _refresh_rate_display(self) -> None:
self.var_rate.set(format_money(self._hourly_rate))
# ------------------------------------------------------------------
# START / STOP
# ------------------------------------------------------------------
def _op_start(self) -> None:
if self._running:
return
# Pre-check: warn if already offline before starting
if not is_connected():
messagebox.showwarning(
"No Internet Connection",
"No active internet connection was detected.\n\n"
"Please connect to the internet before starting a session.",
parent=self.master,
)
return
self._start_dt = datetime.now()
self._running = True
self.var_start.set(self._start_dt.strftime("%H:%M:%S"))
self.var_end.set("")
self.var_duration.set("")
self.var_total.set("")
self.btn_start.config(state=tk.DISABLED)
self.btn_stop.config(state=tk.NORMAL)
self._set_status("Session running ...")
self._tick()
self._net_poll() # start network watchdog
def _op_stop(self, reason: str = "manual") -> None:
if not self._running:
return
# Cancel both timers
for attr in ("_after_id", "_net_id"):
h = getattr(self, attr, None)
if h is not None:
self.master.after_cancel(h)
setattr(self, attr, None)
self._running = False
end_dt = datetime.now()
elapsed_sec = int((end_dt - self._start_dt).total_seconds())
cost = calc_cost(elapsed_sec, self._hourly_rate)
self.var_end.set(end_dt.strftime("%H:%M:%S"))
self.var_duration.set(format_duration(elapsed_sec))
self.var_total.set(format_money(cost))
self.btn_start.config(state=tk.NORMAL)
self.btn_stop.config(state=tk.DISABLED)
if reason == "disconnect":
self._set_status(
"Internet connection lost — session stopped automatically."
)
else:
self._set_status(
f"Session ended. Duration: {format_duration(elapsed_sec)}"
f" | Total: {format_money(cost)}"
)
# ------------------------------------------------------------------
# LIVE TICKER
# ------------------------------------------------------------------
def _tick(self) -> None:
if not self._running:
return
elapsed_sec = int((datetime.now() - self._start_dt).total_seconds())
self.var_duration.set(format_duration(elapsed_sec))
self.var_total.set(format_money(calc_cost(elapsed_sec, self._hourly_rate)))
self._after_id = self.master.after(TICK_MS, self._tick)
# ------------------------------------------------------------------
# NETWORK WATCHDOG
# ------------------------------------------------------------------
def _net_poll(self) -> None:
"""
Poll internet connectivity every NET_CHECK_MS milliseconds.
Runs only while a session is active.
When the connection drops, the session is stopped automatically
and the user is notified with a clear warning dialog.
"""
if not self._running:
return
if not is_connected():
# Stop the session first, then show the dialog
self._op_stop(reason="disconnect")
messagebox.showwarning(
"Internet Connection Lost",
"The internet connection has been interrupted.\n\n"
"The session has been stopped automatically.\n\n"
"Reconnect and press Start to begin a new session.",
parent=self.master,
)
return # do NOT reschedule — session is over
self._net_id = self.master.after(NET_CHECK_MS, self._net_poll)
# ------------------------------------------------------------------
# MISC
# ------------------------------------------------------------------
def _set_status(self, text: str) -> None:
self.lbl_status.config(text=f" {text}")
def _on_quit(self) -> None:
if self._running:
if not messagebox.askyesno(
"Session Active",
"A session is currently running.\n\n"
"Quit without stopping the session?",
icon=messagebox.WARNING, parent=self.master,
):
return
for attr in ("_after_id", "_net_id"):
h = getattr(self, attr, None)
if h is not None:
self.master.after_cancel(h)
self.master.destroy()
def _center_window(self) -> None:
self.master.update_idletasks()
w = self.master.winfo_width()
h = self.master.winfo_height()
sw = self.master.winfo_screenwidth()
sh = self.master.winfo_screenheight()
self.master.geometry(f"+{(sw - w) // 2}+{(sh - h) // 2}")
# ---------------------------------------------------------------------------
# ENTRY POINT
# ---------------------------------------------------------------------------
def main() -> None:
root = tk.Tk()
InternetTimerApp(root)
root.mainloop()
input("\nPress ENTER to exit.")
if __name__ == "__main__":
main()


