Horizontal scroll bar not scrolling tkinter - python-3.x

Having trouble with this horizontal scroll bar... I've pieced this much together but could be completely wrong. The scroll bar is there where I want it to be it's just unusable and wont scroll anywhere.
There are about 83 columns and I can only see 15 of them.
import databasefile
import sqlite3
from tkinter import *
from tkinter.ttk import *
conn = sqlite3.connect("test_db.db")
cursor = conn.cursor()
returns = cursor.execute("SELECT * FROM Order_Number ORDER BY id DESC")
variables = [1,2,3,4,5,6,7,8,9,0,11,12,13,14,15,16,17,18,19,20]
class App(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.CreateUI()
self.grid(sticky = (N,S,W,E))
parent.grid_rowconfigure(1, weight = 1)
parent.grid_columnconfigure(1, weight = 1)
parent.wm_title("Database")
parent.maxsize(width=1200,height=600)
parent.resizable(width=0,height=0)
def CreateUI(self):
tv = Treeview(self)
tv['columns'] = variables
# Scroll Bar section
vsb = Scrollbar(root,orient="horizontal",command=tv.xview)
tv.configure(xscrollcommand=vsb.set)
vsb.place(x=0,y=210,height=20, width=1200)
tv.column("#0", anchor="w",width=25)
for item in variables:
tv.heading(str(item),text=str(item))
tv.column(str(item),anchor='center',width=75)
tv.grid(sticky = (N,S,W,E))
self.grid_rowconfigure(index=1, weight = 1)
self.grid_columnconfigure(index=1, weight = 1)
for row in returns:
tv.insert('','end',values=(row))
root = Tk()
App(root)
root.mainloop()

Related

Fading out multiple objects in pyQT

I'm trying to fade out an entire row of a GridLayout when a user clicks a button. So far I've been able to loop each of the widgets on the specific row which is working but I'm struggling with creating a parallel animation for them all...
idx = self.parent().findChild(QGridLayout).indexOf(self)
location = self.parent().findChild(QGridLayout).getItemPosition(idx)
row, col = location[:2]
self.parent_grid = self.parent().findChild(QGridLayout)
col_count = self.parent().findChild(QGridLayout).columnCount()
print(col_count)
self.effects = []
self.animations = []
anim_group = QParallelAnimationGroup()
for i in range(col_count-1):
effect = QGraphicsOpacityEffect(self)
self.effects.append(effect)
self.parent_grid.itemAtPosition(row,i).widget().setGraphicsEffect(effect)
new_anim = QPropertyAnimation(effect, b"opacity")
new_anim.setDuration(1000)
new_anim.setStartValue(1)
new_anim.setEndValue(0)
new_anim.finished.connect(lambda: self.hide_widget(col_count, row))
self.animations.append(new_anim)
anim_group.addAnimation(new_anim)
anim_group.start()
Since the count of widgets might be high, it's not a good idea to create an animation for each one of them.
Instead, you can use a "controller" that has its own unique animation, and updates the opacity of each widgets in its row.
While using a QPropertyAnimation can work, it would require adding a custom property for that, and it's not really needed for this purpose. Instead, use a basic QVariantAnimation and connect its valueChanged to a function that updates the opacity of each graphics effect.
Here is a possible implementation:
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class RowFadeController(QObject):
def __init__(self, parent, buttons):
super().__init__()
self.buttons = buttons
self.effects = []
for button in buttons:
effect = QGraphicsOpacityEffect(button, opacity=1.0)
button.setGraphicsEffect(effect)
self.effects.append(effect)
self.animation = QVariantAnimation(self)
self.animation.setStartValue(1.)
self.animation.setEndValue(0.)
self.animation.valueChanged.connect(self.setOpacity)
def toggle(self, hide):
self.animation.setDirection(
self.animation.Forward if hide else self.animation.Backward
)
self.animation.start()
def setOpacity(self, opacity):
for effect in self.effects:
effect.setOpacity(opacity)
class Window(QWidget):
def __init__(self):
super().__init__()
layout = QGridLayout(self)
self.fadeControllers = []
for row in range(10):
rowButtons = []
for col in range(10):
button = QPushButton(str(col + 1))
layout.addWidget(button, row, col)
rowButtons.append(button)
toggleButton = QPushButton('toggle', checkable=True)
layout.addWidget(toggleButton, row, layout.columnCount() - 1)
fadeController = RowFadeController(self, rowButtons)
self.fadeControllers.append(fadeController)
toggleButton.clicked.connect(fadeController.toggle)
app = QApplication([])
test = Window()
test.show()
app.exec()
Tweaked for PyQT6 and added a function to get rid of the row once it's faded out.
from PyQt6.QtCore import *
from PyQt6.QtWidgets import *
class RowFadeController(QObject):
def __init__(self, parent, buttons):
super().__init__(parent)
self.buttons = buttons
self.effects = []
for button in buttons:
effect = QGraphicsOpacityEffect(button, opacity=1.0)
button.setGraphicsEffect(effect)
self.effects.append(effect)
self.animation = QVariantAnimation(self)
self.animation.setStartValue(1.)
self.animation.setEndValue(0.)
self.animation.valueChanged.connect(self.setOpacity)
self.animation.finished.connect(self.hideRow)
def toggle(self, hide):
self.animation.setDirection(
self.animation.Direction.Forward if hide else self.animation.Direction.Backward
)
self.animation.start()
def setOpacity(self, opacity):
for effect in self.effects:
effect.setOpacity(opacity)
def hideRow(self):
for button in self.buttons:
button.hide()
button.setParent(None)
class Window(QWidget):
def __init__(self):
super().__init__()
layout = QGridLayout(self)
self.fadeControllers = []
for row in range(10):
rowButtons = []
for col in range(10):
button = QPushButton(str(col + 1))
layout.addWidget(button, row, col)
rowButtons.append(button)
toggleButton = QPushButton('toggle', checkable=True)
rowButtons.append(toggleButton)
layout.addWidget(toggleButton, row, layout.columnCount() - 1)
fadeController = RowFadeController(self, rowButtons)
self.fadeControllers.append(fadeController)
toggleButton.clicked.connect(fadeController.toggle)
app = QApplication([])
test = Window()
test.show()
app.exec()

Tkinter Grid Geometry Resizing issue

Browse Button in Right Hand side Panel is being separated when resizing the window. I want the whole thing to stay together and resizing equally.
import tkinter as tk
from tkinter import ttk
from tkinter import *
class AppLayout(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.masterPane = tk.PanedWindow(self )
self.leftPane = tk.Frame(self.masterPane,relief = 'raised',bg='black',width =100)
self.masterPane.add(self.leftPane)
self.rightPane = tk.Frame(self.masterPane)
self.masterPane.add(self.rightPane)
self.masterPane.pack(fill = 'both',expand = True)
name_entry = tk.Entry(self.rightPane,font =('calibre',10,'normal'))
Browse_Button = tk.Button(self.rightPane,text = 'Browse')
Upload_Button = tk.Button(self.rightPane,text = 'Upload')
name_entry.grid(row=1,column=1)
Browse_Button.grid(row=1,column=2)
Upload_Button.grid(row=1,column=1,pady =(50,0))
self.rightPane.columnconfigure(1, weight=1)
self.rightPane.rowconfigure(1, weight=1)
app = AppLayout()
app.mainloop()
There are four things you need to change:
don't double-import tkinter. It's enough to import * from it.
configure the second column of the rightPane to extend when you resize the window.
Put the button at the western side (W) of the second column by adding sticky=W to the .grid() method.
Glue the Entry widget to the Eastern and Western side of column 1. So, it will get wider when this column extends.
With the following code, it works.
You can also use the .colmnconfigure() and .rowconfigure() method on frame widgets to specify how the other pane extends and how the app resizes vertically.
from tkinter import ttk
from tkinter import *
class AppLayout(Tk):
def __init__(self):
Tk.__init__(self)
self.masterPane = PanedWindow(self )
self.leftPane = Frame(self.masterPane,relief = 'raised',bg='black',width =100)
self.masterPane.add(self.leftPane)
self.rightPane = Frame(self.masterPane)
self.rightPane.columnconfigure(2, weight=1)
self.masterPane.add(self.rightPane)
self.masterPane.pack(fill = 'both',expand = True)
name_entry = Entry(self.rightPane,font =('calibre',10,'normal'))
Browse_Button = Button(self.rightPane,text = 'Browse')
Upload_Button = Button(self.rightPane,text = 'Upload')
name_entry.grid(row=1,column=1,sticky=W+E)
Browse_Button.grid(row=1,column=2, sticky=W)
Upload_Button.grid(row=1,column=1,pady =(50,0))
self.rightPane.columnconfigure(1, weight=1)
self.rightPane.rowconfigure(1, weight=1)
app = AppLayout()
app.mainloop()

Drag Drop List in Tkinter

from tkinter import *
import tkinter as tk
root = tk.Tk()
def add_many_songs():
# Loop thru to fill list box
for song in range(11):
playlist_box.insert(END, song)
playlist_box =tk.Listbox(root,bg="black", fg="green", width=60, selectbackground="green", selectforeground='black',font = 20)
playlist_box.grid(row=0, column=0)
add_many_songs()
class DragDropListbox(tk.Listbox):
""" A Tkinter listbox with drag'n'drop reordering of entries. """
def __init__(self, master, **kw):
kw['selectmode'] = tk.SINGLE
tk.Listbox.__init__(self, master, kw)
self.bind('<Button-1>', self.setCurrent)
self.bind('<B1-Motion>', self.shiftSelection)
self.curIndex = None
def setCurrent(self, event):
self.curIndex = self.nearest(event.y)
def shiftSelection(self, event):
i = self.nearest(event.y)
if i < self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i+1, x)
self.curIndex = i
elif i > self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i-1, x)
self.curIndex = i
##I found this code that does drag and drop features within tkinter list. I got it to work with the example code. However, I am not able to get it to work within the attached code. I am still learning Python.
You should use the class DragDropListbox instead of tk.Listbox when creating playlist_box:
import tkinter as tk
def add_many_songs():
# Loop thru to fill list box
for song in range(11):
playlist_box.insert(tk.END, song)
class DragDropListbox(tk.Listbox):
""" A Tkinter listbox with drag'n'drop reordering of entries. """
def __init__(self, master, **kw):
kw['selectmode'] = tk.SINGLE
tk.Listbox.__init__(self, master, kw)
self.bind('<Button-1>', self.setCurrent)
self.bind('<B1-Motion>', self.shiftSelection)
self.curIndex = None
def setCurrent(self, event):
self.curIndex = self.nearest(event.y)
def shiftSelection(self, event):
i = self.nearest(event.y)
if i < self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i+1, x)
self.curIndex = i
elif i > self.curIndex:
x = self.get(i)
self.delete(i)
self.insert(i-1, x)
self.curIndex = i
root = tk.Tk()
playlist_box = DragDropListbox(root,bg="black", fg="green", width=60, selectbackground="green", selectforeground='black',font = 20)
playlist_box.grid(row=0, column=0)
add_many_songs()
root.mainloop()
Note that it is not recommended to import tkinter like below:
from tkinter import *
import tkinter as tk
Just use import tkinter as tk.

Tkinter display class list text

I have the following piece of code: I am trying to get the text from function showText to actually appear in the window
from tkinter import *
import wikipedia
from nltk.tokenize import sent_tokenize, word_tokenize
import time
subject = wikipedia.page("Assembly language")
#print(p.url)
#print(p.title)
plain_text = subject.content
plain_text_words = word_tokenize(plain_text)
class Window(Frame):
def __init__(self, master= None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.master.title('Test')
self.pack(fill=BOTH, expand =1)
quitButton = Button(self, text = 'Exit', command=self.client_exit)
quitButton.place(x=500, y=300)
menu = Menu(self.master)
self.master.config(menu=menu)
file = Menu(menu)
menu.add_cascade(label='File', menu=file)
file.add_command(label='Run', command=self.showText)
file.add_command(label='Exit', command=self.client_exit)
help_menu = Menu(menu)
menu.add_cascade(label='Help', menu=help_menu)
help_menu.add_command(label='About')
def showText(self):
for i in range (0, len(plain_text_words)):
words = [i]
time.sleep(3)
words.pack()
def client_exit(self):
exit()
root = Tk()
w = 600 # width of the window
h = 370 # height of the window
# get screen width and height
ws = root.winfo_screenwidth()
hs = root.winfo_screenheight()
# calculate x and y coordinates for the Tk root window
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
The piece that's causing me grief is this bit:
def showText(self):
for i in range (0, len(plain_text_words)):
words = [i]
time.sleep(3)
words.pack()
I am trying to get each piece of text to show as individual words on the window but no matter how I try and do it, I get an error. I have tried various things such as converting to lists etc Hoping someone can help..
PS: You will need to download the nltk dataset before this code will run
First of all, your pack function misses the brackets.
Second, your var words is a list. Not a tkinter widget. You can only pack tkinter widgets.
To solve it you need to add the list to a text widget for example.

Scrollbar in tree view in tkinter

I am trying to implement treeview in tkinter with scrollbar. I have got success in placing scrollbar but unable to place it in proper place. Following is full code:
from tkinter import*
from tkinter import ttk
import sqlite3
class Product:
db_name = 'Gateway_Log.db'
def __init__(self,wind):
self.wind=wind
self.wind.title('Device details')
self.tree = ttk.Treeview (height=25, columns=2)
self.tree.heading('#0',text = 'Name', anchor=W)
self.tree.heading(2, text='Price', anchor=W)
vsb = ttk.Scrollbar(wind, orient="vertical")
vsb.configure(command=self.tree.yview)
self.tree.configure(yscrollcommand=vsb.set)
self.tree.pack()
vsb.pack()
self.viewing_records()
def run_query(self, query, parameters =()):
with sqlite3.connect (self.db_name) as conn:
cursor = conn.cursor()
query_result=cursor.execute(query, parameters)
conn.commit()
return query_result
def viewing_records(self):
records = self.tree.get_children()
for element in records:
self.tree.delete(element)
query= 'SELECT * FROM Device_Status'
db_rows=self.run_query(query)
for row in db_rows:
self.tree.insert('',0, text = row[0], values = row[1])
if __name__ == '__main__':
wind=Tk()
application = Product(wind)
wind.mainloop()
Following output I am getting:
I want scrollbar after price field not at bottom side.
You need to indicate the side where you want to pack your widget and also which direction it should fill.
self.tree.pack(side="left")
vsb.pack(side="right",fill="y")

Resources