Compare commits
5 commits
97943aa595
...
24a2bf55af
Author | SHA1 | Date | |
---|---|---|---|
24a2bf55af | |||
277fbd6311 | |||
1e2b3902f2 | |||
f0cfa46f28 | |||
d50cd17f9d |
4 changed files with 168 additions and 19 deletions
88
buypeeb.py
88
buypeeb.py
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
# <one line to give the program's name and a brief idea of what it does.>
|
# buypeeb - a program to track yahoo jp auctions of peebus.
|
||||||
# Copyright (C) 2020 lynnesbian
|
# Copyright (C) 2020 lynnesbian
|
||||||
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
@ -16,16 +16,20 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#hi lynne, peebus here. i love you so much and I think you're doing an excellent job and I'm so proud of you and you're an extremely good partner and please keep being you <3. 0u0\/0u0
|
||||||
|
|
||||||
import requests, gi
|
import requests, gi
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk, Gio, Gdk
|
from gi.repository import Gtk
|
||||||
|
|
||||||
import functions
|
|
||||||
from listing import YahooAuctionsItem
|
|
||||||
from settings import BuypeebSettings
|
|
||||||
|
|
||||||
import json, sys
|
import json, sys
|
||||||
from os import path
|
from os import path
|
||||||
|
from threading import Thread
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import functions
|
||||||
|
from listing import YahooAuctionsItem, JST
|
||||||
|
from settings import BuypeebSettings
|
||||||
|
|
||||||
isWindows = sys.platform.startswith('win')
|
isWindows = sys.platform.startswith('win')
|
||||||
|
|
||||||
|
@ -34,7 +38,29 @@ if isWindows:
|
||||||
else:
|
else:
|
||||||
settingsLocation = path.expanduser("~/.config/Lynnear Software/buypeeb/") # dotfiles in ~ need to die. begone dot
|
settingsLocation = path.expanduser("~/.config/Lynnear Software/buypeeb/") # dotfiles in ~ need to die. begone dot
|
||||||
|
|
||||||
|
class WatchlistUpdater(Thread):
|
||||||
|
def __init__(self, app, update_all: bool = False):
|
||||||
|
Thread.__init__(self)
|
||||||
|
self.app = app
|
||||||
|
self.update_all = update_all
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# TODO: make this happen in parallel
|
||||||
|
if self.update_all:
|
||||||
|
for id, item in self.app.settings.watchlist.items():
|
||||||
|
if not (item.ready or item.updating):
|
||||||
|
item.update()
|
||||||
|
|
||||||
|
else:
|
||||||
|
for item in self.app.settings.outdated_items():
|
||||||
|
if not(item.ready or item.updating):
|
||||||
|
item.update()
|
||||||
|
|
||||||
|
self.app.renderList()
|
||||||
class BuypeebApp:
|
class BuypeebApp:
|
||||||
|
|
||||||
|
# SETUP
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.app = Gtk.Application.new('com.lynnearsoftware.buypeeb', 0)
|
self.app = Gtk.Application.new('com.lynnearsoftware.buypeeb', 0)
|
||||||
self.app.connect('startup', self.startup)
|
self.app.connect('startup', self.startup)
|
||||||
|
@ -64,17 +90,20 @@ class BuypeebApp:
|
||||||
|
|
||||||
def activate(self, app):
|
def activate(self, app):
|
||||||
self.window.show_all()
|
self.window.show_all()
|
||||||
|
self.updateItems()
|
||||||
|
|
||||||
def shutdown(self, app):
|
def shutdown(self, app):
|
||||||
self.settings.save()
|
self.settings.save()
|
||||||
self.app.quit()
|
self.app.quit()
|
||||||
|
|
||||||
def msgBox(self, title, text, form = Gtk.MessageType.WARNING):
|
# CONVENIENCE FUNCTIONS
|
||||||
|
|
||||||
|
def msgBox(self, title, text, form = Gtk.MessageType.WARNING, buttons = Gtk.ButtonsType.OK):
|
||||||
msgbox = Gtk.MessageDialog(
|
msgbox = Gtk.MessageDialog(
|
||||||
parent = self.window,
|
parent = self.window,
|
||||||
flags = 0,
|
flags = 0,
|
||||||
message_type = form,
|
message_type = form,
|
||||||
buttons = Gtk.ButtonsType.OK,
|
buttons = buttons,
|
||||||
text = title
|
text = title
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -116,23 +145,60 @@ class BuypeebApp:
|
||||||
def setExchangeRate(self):
|
def setExchangeRate(self):
|
||||||
self.rate = functions.get_exchange_rate()
|
self.rate = functions.get_exchange_rate()
|
||||||
|
|
||||||
|
def updateItems(self, update_all: bool = False):
|
||||||
|
updater = WatchlistUpdater(self, update_all)
|
||||||
|
updater.start()
|
||||||
|
|
||||||
def renderList(self):
|
def renderList(self):
|
||||||
self.items.clear()
|
self.items.clear()
|
||||||
print(self.settings.watchlist)
|
print(self.settings.watchlist)
|
||||||
for id, item in self.settings.watchlist.items():
|
for id, item in self.settings.watchlist.items():
|
||||||
print("heeh hoo")
|
if item.ready:
|
||||||
self.items.append([item.name, item.price_aud(self.rate), "heenlo", id])
|
self.items.append([item.name, item.price_aud(self.rate), "...", id])
|
||||||
|
else:
|
||||||
|
self.items.append([item.name, "...", "...", id])
|
||||||
|
|
||||||
|
self.updateListTimes()
|
||||||
|
|
||||||
|
def updateListTimes(self):
|
||||||
|
now = datetime.now()
|
||||||
|
ndate = now.strftime("%d %b")
|
||||||
|
for listing in self.items:
|
||||||
|
id = listing[3]
|
||||||
|
item = self.settings.watchlist[id]
|
||||||
|
if item.end_date != None:
|
||||||
|
idate, itime = item.end_date.strftime("%d %b"), item.end_date.strftime("%H:%M")
|
||||||
|
if idate == ndate:
|
||||||
|
listing[2] = itime
|
||||||
|
else:
|
||||||
|
listing[2] = f"{idate} {itime}"
|
||||||
|
|
||||||
|
# BUTTON CLICKS
|
||||||
|
|
||||||
def btnAddClicked(self, widget):
|
def btnAddClicked(self, widget):
|
||||||
url = self.entryBox("Add URL", "Enter the URL of the item you want to add.")
|
url = self.entryBox("Add URL", "Enter the URL of the item you want to add.")
|
||||||
if url:
|
if url:
|
||||||
# vry simpl url validation for simpol creachers
|
# vry simpl url validation for simpol creachers
|
||||||
if functions.rmatch("https?.+yahoo.+", url):
|
if functions.rmatch(r"https?.+yahoo.+", url):
|
||||||
self.settings.watch(url)
|
self.settings.watch(url)
|
||||||
else:
|
else:
|
||||||
self.msgBox("Invalid URL", "The provided URL was invalid.", Gtk.MessageType.ERROR)
|
self.msgBox("Invalid URL", "The provided URL was invalid.", Gtk.MessageType.ERROR)
|
||||||
|
|
||||||
self.renderList()
|
self.renderList()
|
||||||
|
self.updateItems()
|
||||||
|
|
||||||
|
def btnClearEndedClicked(self, widget):
|
||||||
|
if self.msgBox("Clear ended?", "Are you sure you want to clear all ended items from the watchlist?", buttons = Gtk.ButtonsType.OK_CANCEL):
|
||||||
|
for id, item in self.settings.watchlist.items():
|
||||||
|
if not item.available:
|
||||||
|
del self.settings.watchlist[key]
|
||||||
|
|
||||||
|
self.renderList()
|
||||||
|
|
||||||
|
def btnClearAllClicked(self, widget):
|
||||||
|
if self.msgBox("Clear all?", "Are you sure you want to clear all watched items?", buttons = Gtk.ButtonsType.OK_CANCEL):
|
||||||
|
self.settings.watchlist = {}
|
||||||
|
self.items.clear()
|
||||||
|
|
||||||
def btnQuitClicked(self, widget):
|
def btnQuitClicked(self, widget):
|
||||||
prompt = Gtk.MessageDialog(
|
prompt = Gtk.MessageDialog(
|
||||||
|
|
61
listing.py
61
listing.py
|
@ -8,9 +8,59 @@ import functions
|
||||||
JST = timezone(timedelta(hours = 9))
|
JST = timezone(timedelta(hours = 9))
|
||||||
|
|
||||||
class YahooAuctionsItem:
|
class YahooAuctionsItem:
|
||||||
|
"""
|
||||||
|
A class for handling items on Yahoo! Auctions Japan
|
||||||
|
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
The name given to the auction item by the user.
|
||||||
|
|
||||||
|
original_name : str
|
||||||
|
The name as it appears on Y!A,
|
||||||
|
|
||||||
|
favourite : bool
|
||||||
|
Whether or not the user has "favourited" this item.
|
||||||
|
|
||||||
|
url : bool
|
||||||
|
The URL this item was added from. If not provided, defaults to https://buyee.jp/item/yahoo/auction/{id}.
|
||||||
|
|
||||||
|
id : str
|
||||||
|
The ID of the item, found at the end of the URL.
|
||||||
|
|
||||||
|
last_checked : datetime
|
||||||
|
The time that the item's status was last checked by buypeeb.
|
||||||
|
|
||||||
|
available : bool
|
||||||
|
Whether or not the item is still available (if the auction is still available)
|
||||||
|
|
||||||
|
ready : bool
|
||||||
|
Whether or not the object is ready. If not, some of the data (price, available, etc.) may be out of date or unset.
|
||||||
|
|
||||||
|
updating : bool
|
||||||
|
If true, the object is currently being updated. Should never be true if ready is true.
|
||||||
|
|
||||||
|
price : float
|
||||||
|
The price of the item in yen.
|
||||||
|
|
||||||
|
bids : int
|
||||||
|
The number of bids that have been placed on the item.
|
||||||
|
|
||||||
|
start_date : datetime
|
||||||
|
The start date and time of the item in JST.
|
||||||
|
|
||||||
|
end_date : datetime
|
||||||
|
The end date and time of the item in JST.
|
||||||
|
"""
|
||||||
def __init__(self, url: str, id: str, name: str = None, from_json: dict = None):
|
def __init__(self, url: str, id: str, name: str = None, from_json: dict = None):
|
||||||
# note - incoming url is not validated in any way!
|
# note - incoming url is not validated in any way!
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.price = 0
|
||||||
|
self.original_name = None
|
||||||
|
self.favourite = None
|
||||||
|
self.end_date = None
|
||||||
|
|
||||||
if url == None and from_json != None and id != None:
|
if url == None and from_json != None and id != None:
|
||||||
self.id = id
|
self.id = id
|
||||||
self.name = from_json['name']
|
self.name = from_json['name']
|
||||||
|
@ -27,15 +77,17 @@ class YahooAuctionsItem:
|
||||||
|
|
||||||
self.last_checked = datetime.fromisoformat('1970-01-01')
|
self.last_checked = datetime.fromisoformat('1970-01-01')
|
||||||
self.available = True
|
self.available = True
|
||||||
self.update()
|
self.ready = False
|
||||||
|
self.updating = False
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
self.updating = True
|
||||||
try:
|
try:
|
||||||
# the good news is, yahoo japan returns all the data we need in handy json format
|
# the good news is, yahoo japan returns all the data we need in handy json format
|
||||||
# the bad news is that the only way to get that json format is to download the whole auction page and grep it
|
# the bad news is that the only way to get that json format is to download the whole auction page and grep it
|
||||||
|
|
||||||
# r = requests.get(f"https://page.auctions.yahoo.co.jp/jp/auction/{self.id}").text
|
r = requests.get(f"https://page.auctions.yahoo.co.jp/jp/auction/{self.id}").text
|
||||||
r = open("yahoo.html").read()
|
# r = open("yahoo.html").read()
|
||||||
j = json.loads(re.match(r'.*var pageData ?= ?(\{.*?\});', r, re.DOTALL).group(1))
|
j = json.loads(re.match(r'.*var pageData ?= ?(\{.*?\});', r, re.DOTALL).group(1))
|
||||||
except:
|
except:
|
||||||
raise
|
raise
|
||||||
|
@ -50,6 +102,9 @@ class YahooAuctionsItem:
|
||||||
if self.name == None:
|
if self.name == None:
|
||||||
self.name = j['productName']
|
self.name = j['productName']
|
||||||
|
|
||||||
|
self.ready = True
|
||||||
|
self.updating = False
|
||||||
|
|
||||||
def price_jpy(self):
|
def price_jpy(self):
|
||||||
return f"¥{self.price:.2f}"
|
return f"¥{self.price:.2f}"
|
||||||
|
|
||||||
|
|
18
settings.py
18
settings.py
|
@ -1,8 +1,9 @@
|
||||||
import json, os, pickle
|
import json, os, pickle
|
||||||
from os import path
|
from os import path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import functions
|
import functions
|
||||||
from listing import YahooAuctionsItem
|
from listing import YahooAuctionsItem, JST
|
||||||
|
|
||||||
class BuypeebSettings:
|
class BuypeebSettings:
|
||||||
def __init__(self, location: str):
|
def __init__(self, location: str):
|
||||||
|
@ -50,3 +51,18 @@ class BuypeebSettings:
|
||||||
self.watchlist[id] = YahooAuctionsItem(url, id, name)
|
self.watchlist[id] = YahooAuctionsItem(url, id, name)
|
||||||
for item in self.watchlist.values():
|
for item in self.watchlist.values():
|
||||||
print(item.name, item.price)
|
print(item.name, item.price)
|
||||||
|
|
||||||
|
def outdated_items(self):
|
||||||
|
out = []
|
||||||
|
now = datetime.now(tz = JST).timestamp()
|
||||||
|
for item in self.watchlist.values():
|
||||||
|
time_difference = now - item.last_checked.timestamp()
|
||||||
|
ending_soon = False # TODO: this
|
||||||
|
|
||||||
|
if (item.favourite and time_difference >= self.favouriteUpdateInterval) or \
|
||||||
|
(item.favourite and ending_soon and time_difference >= self.favouriteUpdateIntervalCritical) or \
|
||||||
|
(time_difference >= self.updateInterval) or \
|
||||||
|
(ending_soon and time_difference >= self.updateIntervalCritical):
|
||||||
|
out.append(item)
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Add new</property>
|
||||||
<property name="label" translatable="yes">Add new</property>
|
<property name="label" translatable="yes">Add new</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-add</property>
|
<property name="stock_id">gtk-add</property>
|
||||||
|
@ -63,6 +64,7 @@
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Update all</property>
|
||||||
<property name="label" translatable="yes">Update all</property>
|
<property name="label" translatable="yes">Update all</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-refresh</property>
|
<property name="stock_id">gtk-refresh</property>
|
||||||
|
@ -87,6 +89,7 @@
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Undo</property>
|
||||||
<property name="label" translatable="yes">Undo</property>
|
<property name="label" translatable="yes">Undo</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-undo</property>
|
<property name="stock_id">gtk-undo</property>
|
||||||
|
@ -101,6 +104,7 @@
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Redo</property>
|
||||||
<property name="label" translatable="yes">Redo</property>
|
<property name="label" translatable="yes">Redo</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-redo</property>
|
<property name="stock_id">gtk-redo</property>
|
||||||
|
@ -122,12 +126,14 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton" id="btnClearEnded">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Clear ended</property>
|
||||||
<property name="label" translatable="yes">Clear ended</property>
|
<property name="label" translatable="yes">Clear ended</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-clear</property>
|
<property name="stock_id">gtk-clear</property>
|
||||||
|
<signal name="clicked" handler="btnClearEndedClicked" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
|
@ -135,12 +141,14 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton" id="btnClearAll">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Clear all</property>
|
||||||
<property name="label" translatable="yes">Clear all</property>
|
<property name="label" translatable="yes">Clear all</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-delete</property>
|
<property name="stock_id">gtk-delete</property>
|
||||||
|
<signal name="clicked" handler="btnClearAllClicked" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
|
@ -161,6 +169,7 @@
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Open</property>
|
||||||
<property name="label" translatable="yes">Open</property>
|
<property name="label" translatable="yes">Open</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-open</property>
|
<property name="stock_id">gtk-open</property>
|
||||||
|
@ -175,6 +184,7 @@
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Export as...</property>
|
||||||
<property name="label" translatable="yes">Export as...</property>
|
<property name="label" translatable="yes">Export as...</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-save-as</property>
|
<property name="stock_id">gtk-save-as</property>
|
||||||
|
@ -199,6 +209,7 @@
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Help</property>
|
||||||
<property name="label" translatable="yes">Help</property>
|
<property name="label" translatable="yes">Help</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-help</property>
|
<property name="stock_id">gtk-help</property>
|
||||||
|
@ -213,6 +224,7 @@
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Settings</property>
|
||||||
<property name="label" translatable="yes">Settings</property>
|
<property name="label" translatable="yes">Settings</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-preferences</property>
|
<property name="stock_id">gtk-preferences</property>
|
||||||
|
@ -227,6 +239,7 @@
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Quit</property>
|
||||||
<property name="label" translatable="yes">Quit</property>
|
<property name="label" translatable="yes">Quit</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="stock_id">gtk-quit</property>
|
<property name="stock_id">gtk-quit</property>
|
||||||
|
@ -272,7 +285,6 @@
|
||||||
<property name="title" translatable="yes">Name</property>
|
<property name="title" translatable="yes">Name</property>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
<property name="clickable">True</property>
|
<property name="clickable">True</property>
|
||||||
<property name="sort_indicator">True</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkCellRendererText"/>
|
<object class="GtkCellRendererText"/>
|
||||||
<attributes>
|
<attributes>
|
||||||
|
@ -296,7 +308,7 @@
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkTreeViewColumn">
|
<object class="GtkTreeViewColumn">
|
||||||
<property name="resizable">True</property>
|
<property name="resizable">True</property>
|
||||||
<property name="title" translatable="yes">Ending in</property>
|
<property name="title" translatable="yes">Ending at</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkCellRendererText"/>
|
<object class="GtkCellRendererText"/>
|
||||||
<attributes>
|
<attributes>
|
||||||
|
|
Loading…
Reference in a new issue