cs2tracker 2.1.4__py3-none-any.whl → 2.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cs2tracker might be problematic. Click here for more details.

cs2tracker/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.1.4'
21
- __version_tuple__ = version_tuple = (2, 1, 4)
20
+ __version__ = version = '2.1.6'
21
+ __version_tuple__ = version_tuple = (2, 1, 6)
cs2tracker/application.py CHANGED
@@ -1,15 +1,37 @@
1
- import os
2
- import subprocess
3
1
  import tkinter as tk
2
+ from subprocess import Popen
3
+ from threading import Thread
4
4
  from typing import cast
5
5
 
6
6
  import matplotlib.pyplot as plt
7
7
  from matplotlib.axes import Axes
8
8
  from matplotlib.dates import DateFormatter
9
9
 
10
- from cs2tracker.constants import CONFIG_FILE, OUTPUT_FILE, TEXT_EDITOR
10
+ from cs2tracker.constants import (
11
+ CONFIG_FILE,
12
+ OS,
13
+ OUTPUT_FILE,
14
+ PYTHON_EXECUTABLE,
15
+ RUNNING_IN_EXE,
16
+ TEXT_EDITOR,
17
+ OSType,
18
+ )
11
19
  from cs2tracker.scraper import Scraper
12
20
 
21
+ WINDOW_TITLE = "CS2Tracker"
22
+ WINDOW_SIZE = "450x380"
23
+ BACKGROUND_COLOR = "#1e1e1e"
24
+ BUTTON_COLOR = "#3c3f41"
25
+ BUTTON_HOVER_COLOR = "#505354"
26
+ BUTTON_ACTIVE_COLOR = "#5c5f61"
27
+ FONT_STYLE = "Segoe UI"
28
+ FONT_COLOR = "white"
29
+
30
+ SCRAPER_WINDOW_TITLE = "CS2Tracker"
31
+ SCRAPER_WINDOW_HEIGHT = 40
32
+ SCRAPER_WINDOW_WIDTH = 100
33
+ SCRAPER_WINDOW_BACKGROUND_COLOR = "Black"
34
+
13
35
 
14
36
  class Application:
15
37
  def __init__(self):
@@ -22,88 +44,164 @@ class Application:
22
44
  application_window = self._configure_window()
23
45
  application_window.mainloop()
24
46
 
47
+ def _add_button(self, frame, text, command):
48
+ """Create and style a button for the main application window."""
49
+ button_style = {
50
+ "font": (FONT_STYLE, 12),
51
+ "fg": FONT_COLOR,
52
+ "bg": BUTTON_COLOR,
53
+ "activebackground": BUTTON_ACTIVE_COLOR,
54
+ }
55
+ button = tk.Button(frame, text=text, command=command, **button_style)
56
+ button.pack(pady=5, fill="x")
57
+ button.bind("<Enter>", lambda _: button.config(bg=BUTTON_HOVER_COLOR))
58
+ button.bind("<Leave>", lambda _: button.config(bg=BUTTON_COLOR))
59
+ return button
60
+
25
61
  def _configure_window(self):
26
- """Configure the main application window layout with buttons for the various
27
- actions.
62
+ """Configure the main application window UI and add buttons for the main
63
+ functionalities.
28
64
  """
29
65
  window = tk.Tk()
30
- window.title("CS2Tracker")
31
- window.geometry("450x450")
32
-
33
- label = tk.Label(window, text="Welcome to CS2Tracker!")
34
- run_button = tk.Button(window, text="Run!", command=self._scrape_prices)
35
- edit_button = tk.Button(window, text="Edit Config", command=self._edit_config)
36
- plot_button = tk.Button(window, text="Show History (Chart)", command=self._draw_plot)
37
- plot_file_button = tk.Button(
38
- window, text="Show History (File)", command=self._edit_log_file
66
+ window.title(WINDOW_TITLE)
67
+ window.geometry(WINDOW_SIZE)
68
+ window.configure(bg=BACKGROUND_COLOR)
69
+
70
+ frame = tk.Frame(window, bg=BACKGROUND_COLOR, padx=30, pady=30)
71
+ frame.pack(expand=True, fill="both")
72
+
73
+ label = tk.Label(
74
+ frame,
75
+ text=f"Welcome to {WINDOW_TITLE}!",
76
+ font=(FONT_STYLE, 16, "bold"),
77
+ fg=FONT_COLOR,
78
+ bg=BACKGROUND_COLOR,
39
79
  )
80
+ label.pack(pady=(0, 30))
81
+
82
+ self._add_button(frame, "Run!", self.scrape_prices)
83
+ self._add_button(frame, "Edit Config", self._edit_config)
84
+ self._add_button(frame, "Show History (Chart)", self._draw_plot)
85
+ self._add_button(frame, "Show History (File)", self._edit_log_file)
86
+
40
87
  background_checkbox_value = tk.BooleanVar(value=self.scraper.identify_background_task())
41
88
  background_checkbox = tk.Checkbutton(
42
- window,
89
+ frame,
43
90
  text="Daily Background Calculation",
44
- command=lambda: self._toggle_background_task(background_checkbox_value.get()),
45
91
  variable=background_checkbox_value,
92
+ command=lambda: self._toggle_background_task(background_checkbox_value.get()),
93
+ bg=BACKGROUND_COLOR,
94
+ fg=FONT_COLOR,
95
+ selectcolor=BUTTON_COLOR,
96
+ activebackground=BACKGROUND_COLOR,
97
+ font=(FONT_STYLE, 10),
46
98
  )
47
-
48
- label.grid(row=0, column=0, pady=50, sticky="NSEW")
49
- run_button.grid(row=1, column=0, pady=10, sticky="NSEW")
50
- edit_button.grid(row=2, column=0, pady=10, sticky="NSEW")
51
- plot_button.grid(row=3, column=0, pady=10, sticky="NSEW")
52
- plot_file_button.grid(row=4, column=0, pady=10, sticky="NSEW")
53
- background_checkbox.grid(row=5, column=0, pady=10, sticky="NS")
54
-
55
- window.grid_columnconfigure(0, weight=1)
56
- window.grid_rowconfigure(1, weight=1)
57
- window.grid_rowconfigure(2, weight=1)
58
- window.grid_rowconfigure(3, weight=1)
59
- window.grid_rowconfigure(4, weight=1)
60
- window.grid_rowconfigure(5, weight=1)
61
-
62
- label.grid_configure(sticky="NSEW")
63
- run_button.grid_configure(sticky="NSEW")
64
- edit_button.grid_configure(sticky="NSEW")
65
- plot_button.grid_configure(sticky="NSEW")
66
- plot_file_button.grid_configure(sticky="NSEW")
67
- background_checkbox.grid_configure(sticky="NSEW")
99
+ background_checkbox.pack(pady=20)
68
100
 
69
101
  return window
70
102
 
71
- def _scrape_prices(self):
103
+ def _construct_scraper_command_windows(self):
104
+ """Construct the command to run the scraper in a new window for Windows."""
105
+ set_utf8_encoding = (
106
+ "[Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.Encoding]::UTF8;"
107
+ )
108
+ get_size = "$size = $Host.UI.RawUI.WindowSize;"
109
+ set_size = "$Host.UI.RawUI.WindowSize = $size;"
110
+ set_window_title = f"$Host.UI.RawUI.WindowTitle = '{SCRAPER_WINDOW_TITLE}';"
111
+ set_window_width = (
112
+ f"$size.Width = [Math]::Min({SCRAPER_WINDOW_WIDTH}, $Host.UI.RawUI.BufferSize.Width);"
113
+ )
114
+ set_window_height = f"$size.Height = {SCRAPER_WINDOW_HEIGHT};"
115
+ set_background_color = (
116
+ f"$Host.UI.RawUI.BackgroundColor = '{SCRAPER_WINDOW_BACKGROUND_COLOR}';"
117
+ )
118
+ clear = "Clear-Host;"
119
+
120
+ if RUNNING_IN_EXE:
121
+ # The python executable is set as the executable itself in PyInstaller
122
+ scraper_cmd = f"{PYTHON_EXECUTABLE} --only-scrape | Out-Host -Paging"
123
+ else:
124
+ scraper_cmd = f"{PYTHON_EXECUTABLE} -m cs2tracker --only-scrape"
125
+
126
+ cmd = (
127
+ 'start powershell -NoExit -Command "& {'
128
+ + set_utf8_encoding
129
+ + set_window_title
130
+ + get_size
131
+ + set_window_width
132
+ + set_window_height
133
+ + set_size
134
+ + set_background_color
135
+ + clear
136
+ + scraper_cmd
137
+ + '}"'
138
+ )
139
+ return cmd
140
+
141
+ def _construct_scraper_command(self):
142
+ """Construct the command to run the scraper in a new window."""
143
+ if OS == OSType.WINDOWS:
144
+ return self._construct_scraper_command_windows()
145
+ else:
146
+ # TODO: Implement for Linux
147
+ return ""
148
+
149
+ def scrape_prices(self):
72
150
  """Scrape prices from the configured sources, print the total, and save the
73
151
  results to a file.
74
152
  """
75
- self.scraper.scrape_prices()
153
+ if OS == OSType.WINDOWS:
154
+ scraper_cmd = self._construct_scraper_command()
155
+ Popen(scraper_cmd, shell=True)
156
+ else:
157
+ # TODO: implement external window for Linux
158
+ self.scraper.scrape_prices()
76
159
 
77
160
  def _edit_config(self):
78
161
  """Edit the configuration file using the specified text editor."""
79
- subprocess.call([TEXT_EDITOR, CONFIG_FILE])
80
- self.scraper.parse_config()
162
+ _popen_and_call(
163
+ popen_args={"args": [TEXT_EDITOR, CONFIG_FILE], "shell": True},
164
+ callback=self.scraper.parse_config,
165
+ )
81
166
 
82
167
  def _draw_plot(self):
83
168
  """Draw a plot of the scraped prices over time."""
84
169
  dates, dollars, euros = self.scraper.read_price_log()
85
170
 
86
- fig, ax_raw = plt.subplots()
87
- ax = cast(Axes, ax_raw)
171
+ fig, ax_raw = plt.subplots(figsize=(10, 8), num="CS2Tracker Price History")
172
+ fig.suptitle("CS2Tracker Price History", fontsize=16)
173
+ fig.autofmt_xdate()
88
174
 
175
+ ax = cast(Axes, ax_raw)
89
176
  ax.plot(dates, dollars, label="Dollars")
90
177
  ax.plot(dates, euros, label="Euros")
91
- ax.set_xlabel("Date")
92
- ax.set_ylabel("Price")
93
178
  ax.legend()
94
-
95
179
  date_formatter = DateFormatter("%d-%m-%Y")
96
180
  ax.xaxis.set_major_formatter(date_formatter)
97
- fig.autofmt_xdate()
98
181
 
99
182
  plt.show()
100
183
 
101
184
  def _edit_log_file(self):
102
185
  """Opens the file containing past price calculations."""
103
- if not os.path.isfile(OUTPUT_FILE):
104
- open(OUTPUT_FILE, "w", encoding="utf-8").close()
105
- subprocess.call([TEXT_EDITOR, OUTPUT_FILE])
186
+ Popen([TEXT_EDITOR, OUTPUT_FILE], shell=True)
106
187
 
107
188
  def _toggle_background_task(self, enabled: bool):
108
189
  """Toggle whether a daily price calculation should run in the background."""
109
190
  self.scraper.toggle_background_task(enabled)
191
+
192
+
193
+ def _popen_and_call(popen_args, callback):
194
+ """
195
+ Runs the given args in a subprocess.Popen, and then calls the function callback when
196
+ the subprocess completes.
197
+
198
+ Source: https://stackoverflow.com/questions/2581817/python-subprocess-callback-when-cmd-exits
199
+ """
200
+
201
+ def process_and_callback(popen_args, callback):
202
+ process = Popen(**popen_args)
203
+ process.wait()
204
+ callback()
205
+
206
+ thread = Thread(target=process_and_callback, args=(popen_args, callback), daemon=True)
207
+ thread.start()