How to restrict movement to a single folder in FileChooser? - python

I'm using python and Kivy for making a simple text editor almost like VSCode. I have come upon the amazing FileChooser widget (more specifically FileChooserListView) and have been using it for file navigation.
However I want to restrict movement to a single project file much like in VSCode, to ensure a safer experience.
I want to do this by making a terminal command at first (like in VSCode: code folder_name), but since that's another topic decided to just use a string main_path (in kv code which you can edit) to which the program must restrict itself to. I will add terminal functionality to choose paths later (if possible guides on that subject would be very helpful).
I really don't know much about Kivy and the documentation is pretty confusing. So I didn't know what all to try.
#######KV FILE name: gridlayout.kv#######
#:import isdir kivytest.GridLayoutApp
#:set main_path '~/Projects'
<Label>:
font_name:'Fura Code Retina Nerd Font Complete.otf'
font_size:'18sp'
<GridLayout>:
cols:3
rows:1
Label:
canvas.before:
Color:
rgb:0.2,0.2,0.2
Rectangle:
pos: self.pos
size: self.size
size_hint_x: None
width: 50
Splitter:
id:splitter
sizable_from: 'right'
min_size: 170
max_size: 400
on_release: app.ask_update()
width:335
FileChooserListView:
id:filechooser
path: main_path
size_hint_x: None
width: splitter.width-splitter.strip_size
font_name: 'Fura Code Retina Nerd Font Complete.otf'
font_size: '1sp'
canvas.before:
Color:
rgb: .17,.19,.19
Rectangle:
pos: self.pos
size: self.size
on_selection: root.select(*args)
background_color:(0.17,0.19,0.17,1)
ScrollView:
id:scroller
TextInput:
id:ti
size_hint: (None, None)
width: scroller.width
height:max(self.minimum_height, scroller.height)
font_size: '18sp'
cursor_color: [255,255,255,1]
background_color: (.17, .18, .17, 1)
foreground_color:[255,255,255,1]
font_name: 'Fura Code Retina Nerd Font Complete.otf'
selection_color: (1,1,1,0.125)
on_text: app.text_changed()
#######Python Code#######
import os
from pathlib import Path
import kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
class GridLayoutApp(App):
file = ''
dir = ''
text = ''
def build(self):
self.grid=GridLayout()
return self.grid
def text_changed(self, *args):
width_calc = self.grid.ids.scroller.width
for line_label in self.grid.ids.ti._lines_labels:
width_calc = max(width_calc, line_label.width + 20)
self.grid.ids.ti.width = width_calc
def ask_update(self, *args):
width_calc = self.grid.ids.splitter.width
self.grid.ids.filechooser.width = width_calc- self.grid.ids.splitter.strip_size
if __name__ == "__main__":
glApp = GridLayoutApp()
glApp.run()
Expected results: Restriction of file navigation outside of the chosen folder (main_path).
EDIT: You can change the font name as well in case you don't have the font.

Related

How to manage kivy file?

when we use kivy to create interface , the kivy file content of many lines of code,
For example we have button with specific design, if I want use this button 10 times for each time should I write of properties of button ?
there is any way to manage the file? or we can make more than one kivy file to manage and easy understands code of kivy?
first define the class in the python file like
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior, RectangularRippleBehavior
from kivy.uix.behaviors import ButtonBehavior
from kivymd.uix.boxlayout import MDBoxLayout
class CustomButon(RoundedRectangularElevationBehavior, RectangularRippleBehavior, ButtonBehaviour, MDBoxLayout):
text = StringProperty()
def fxn_to_call_on_release(self):
pass
and your kv file should look somewhat like this when defining the properties of the button
<CustomButton>:
size_hint: None, None
width: dp(80)
height: dp(32)
elevation: 20
radius: [dp(15), dp(15), dp(15), dp(15)]
canvas.before:
Color:
rgba: gch("#f3a635")
RoundedRectangle:
size: self.size
pos: self.pos
radius: [dp(15), dp(15), dp(15), dp(15)]
MDLabel:
text: root.text,
halign: "center",
font_style: "Button",
opposite_colors: True,
bold: True
The properties defined are my preferences, you can edit as you see fit.
with these definitions you can call the button in other widgets;
CustomButton:
on_release: self.fxn_to_call_on_release()
text: "Button Text"
you can call the button like this as many times as you wish.

Background rectangle fit to label text in Kivy

I am trying to get a background around a label that fits the number of lines in the texted in it. For example, a label with text 'Line1\nLine1\nLine3' would have a larger Y dimension that just 'line1'
What currently happens is all of the labels are the same size and clip-off text that doesn't fit within them, the labels are also inside a recycleview layout because I would like to be able to scroll and update large amount of the labels often.
I have tried a few things but have had no luck, and am struggling to get a variable to be understood where I have added # HERE in the .kv file
from kivy.app import App
from kivy.properties import NumericProperty, Clock, ObjectProperty, StringProperty
from kivy.uix.widget import Widget
from kivy.uix.recycleview import RecycleView
from kivy.uix.button import Button
from kivy.uix.label import Label
class TopPostsTest(RecycleView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
message_height = NumericProperty(20)
items = ["hello\ntest\ntest", "test, gghgjhgjhgjhgjhghjghjgjhgjhgjhgjhgjhgjhgjhgjhg", "cheese"]
self.data = [{'text':str(p)} for p in items]
class LabelColor(Label):
pass
class TruthApp(App):
# def build(self):
# return super().build()
pass
if __name__ == "__main__":
TruthApp().run()
<MainControl#PageLayout>:
border: "25dp"
swipe_threshold: 0.4
TopPostsTest:
Settings:
<LabelColor>:
color: 0,0,0,1
text_size: self.size
halign: 'left'
valign: 'top'
font_name: "Assets/Fonts/Nunito-Bold.ttf"
font_size: "12dp"
multiline: True
canvas.before:
Color:
rgba: (0.8, 0.8, 0.8, 1)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [5, 5, 5, 5]
canvas:
Color:
rgba:0,0.9,0.9,1
Line:
width:0.8
rounded_rectangle:(self.x,self.y,self.width,root.height, 5) # HERE
<TopPostsTest>:
viewclass: 'LabelColor'
scroll_y: 1
RecycleBoxLayout:
id: message_view
default_size: None, dp(40) # NewHERE
default_size_hint: 1, None
size_hint_y: None
padding: ["10dp", "16dp"]
spacing: "8dp"
height: self.minimum_height
orientation: 'vertical'
Thank you for any help :)
Edit:
I have found that I have been changing the wrong value and that the variable that needs changing has been marker with # NewHERE, however I am still unable to get it to work or get a variable from the py file into the kv
In order to get a Label that expands vertically as it's text-content grows you can set its height to its texture height.
Also, to fit the text within available space (width) you can change text_size.
Thus you can modify the kvlang for LabelColor as,
<LabelColor>:
color: 0,0,0,1
text_size: self.width, None
size_hint_y: None
height: self.texture_size[1]

How to target canvas.before in kv

In a button i have made a rounded button with canvas.before, and it changes colors as it should. The line is:
canvas.before:
Color:
rgba: btn_color_not_pressed if self.state=='normal' else btn_color_pressed
RoundedRectangle:
size: self.size
pos: self.pos
radius: [40]
The variables btn_color_not_pressed and btn_color_not_pressed are made with #:set in the start of the kv-file
I have tried to target the line with self.canvas.before.Color.rgba, as i am used to normally, but i get following error:
AttributeError: 'kivy.graphics.instructions.CanvasBase' object has no attribute 'Color'
How do i target that line from within kv and replace the variables ... or if necessary from the python file.?
How do i target the source: "some_file.jpg under Rectangle?
My goal is that when a user has clicked an option all the button colors (and maybe the background) in the app must change.
You cannot change the variables created in kv. Once your app is running, those variables no longer exist. You can, however, use a Property that is created in kv (or python) as an attribute of a class (or the App itself). Such Properties continue to exist while the App is running, and kivy recognizes such Properties and will automatically handle changes to those Properties. An example is to create a new class that extends Button and has Properties like you want:
<-MyButton#Button>:
# create the desired properties
btn_color_not_pressed: [.5, .5, .5,1]
btn_color_pressed: [.25, .25, .25, 1]
canvas:
Color:
# reference the above properties
rgba: self.btn_color_not_pressed if self.state=='normal' else self.btn_color_pressed
RoundedRectangle:
size: self.size
pos: self.pos
radius: [40]
# this is copied from style.kv to show the Button text
Color:
rgba: 1, 1, 1, 1
Rectangle:
texture: self.texture
size: self.texture_size
pos: int(self.center_x - self.texture_size[0] / 2.), int(self.center_y - self.texture_size[1] / 2.)
# actually make an instance of the new MyButton class
MyButton:
btn_color_not_pressed: [1,0,0,1]
btn_color_pressed: [0,1,0,1]
text: 'Button Test'
The <-MyButton#Button> creates a new class (MyButton) that extends Button. The prepended - indicates that the default canvas instructions for Button are not to be used and the provided instructions are used instead. Those new Properties can be changed in python code as usual. You can use a similar approach for the source property.
That brought me a step closer.
My problem now is that the colors only change the button itself or togglebutton-group, but only when you click on them. It only reacts to the new colors when activated (button og group).
The design is not updated
I tried the solution with the - but it made no difference
main.py
import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.factory import Factory
kivy.require("1.11.1")
class Controller(BoxLayout):
def __init__(self):
super(Controller, self).__init__()
ToggleButtonBehavior.allow_no_selection = False
def change_button_color(self,theme):
if theme == "red":
Factory.My_tgl_btn.btn_color_pressed = (1,0,0,.7)
Factory.My_tgl_btn.btn_color_not_pressed = (1,0,0,.5)
else: # theme == "blue":
Factory.My_tgl_btn.btn_color_pressed = (0,0,1,.7)
Factory.My_tgl_btn.btn_color_not_pressed = (0,0,1,.5)
class mainApp(App):
def build(self):
return Controller()
if __name__ == "__main__":
mainApp().run()
main.kv
#:set bg_color 1,1,1,.7
#:set txt_color 0,0,0,1
#:import Factory kivy.factory.Factory
<Controller>
BoxLayout:
orientation: "vertical"
background_color: 1,1,1,.5
background_normal: ""
Label:
text: "THIS IS THE MAIN TEKST"
color: txt_color
size_hint_y:.7
BoxLayout:
size_hint_y: .15
My_tgl_btn:
text: "RED theme"
group: 1
state: "down"
on_press: root.change_button_color("red")
on_release: root.change_button_color("red")
My_tgl_btn:
text: "Blue theme"
group: 1
on_press: root.change_button_color("blue")
on_release: root.change_button_color("blue")
BoxLayout:
size_hint_y: .15
My_tgl_btn:
text: "Option1"
group: 2
state: "down"
My_tgl_btn:
text: "Option2"
group: 2
state: "normal"
<My_tgl_btn#ToggleButton>
btn_color_pressed: 1,0,0,.7
btn_color_not_pressed: 1,0,0,.5
color: txt_color
background_color: 0,0,0,0
background_normal: ""
canvas.before:
Color:
rgba:self.btn_color_not_pressed if self.state=='normal' else self.btn_color_pressed
RoundedRectangle:
size: self.size
pos: self.pos
radius: [40]
Found a solution (here: Kivy: resetting toggle buttons to "normal" on re-entering screen)
It's kind'a ugly, but it works..
Give every button an id ... and then use on_enter for each button and set and change the state.
In the code above it would mean:
on_enter:
button1.state = "down"
button1.state = "normal"
button2.state = "down"
button2.state = "normal"
button3.state = "down"
button3.state = "normal"
button4.state = "down"
button4.state = "normal"
It works ... but it is not pretty :|

Why does the kivy labels text show out of the label?

I am new to kivy. I want to insert a text into a kivy label at the startup. But the text of the lable shows out of the label as shown below. I can't find a way to fix this. So please give me a solution.
This is the code of the kv file.
<SmoothLabel#Label>
background_color: (0,0,0,0)
background_normal: ''
back_color: (255,255,255,1)
border_radius: [18]
canvas.before:
Color:
rgba: (255,255,255,0.3)
RoundedRectangle:
size: 50,50
pos: 100,10
radius: self.border_radius
<Money_Manager>
FloatLayout:
size_hint_y: None
height:100
Image:
source:'image4.png'
size: self . texture_size
allow_stretch: True
keep_ratio: False
SmoothLabel:
d: Total_Wealth
text: "Total_Wealth"
This is the code of the python file.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
Builder.load_file('total_wealth.kv')
class Money_Manager(App, FloatLayout):
def build(self):
return self
Money_Manager().run()
In your kv file, you have set the pos and size of the RoundedRectangle to fixed values. You need to set them to the pos and size of the Label. So change this:
<SmoothLabel#Label>
background_color: (0,0,0,0)
background_normal: ''
back_color: (255,255,255,1)
border_radius: [18]
canvas.before:
Color:
rgba: (255,255,255,0.3)
RoundedRectangle:
size: 50,50
pos: 100,10
radius: self.border_radius
to:
<SmoothLabel#Label>
background_color: (0,0,0,0)
background_normal: ''
back_color: (255,255,255,1)
border_radius: [18]
canvas.before:
Color:
rgba: (255,255,255,0.3)
RoundedRectangle:
size: self.size
pos: self.pos
radius: self.border_radius
Here is my answer for yours
# import kivy module
import kivy
# this restricts the kivy version i.e
# below this kivy version you cannot use the app or software
kivy.require("1.9.1")
# base Class of your App inherits from the App class.
# app:always refers to the instance of your application
from kivy.app import App
# if you not import label and use it it through error
from kivy.uix.label import Label
# defining the App class
class MyLabelApp(App):
def build(self):
# label display the text on screen
lbl = Label(text ="Label is Added on screen !!:):)")
return lbl
# creating the object
label = MyLabelApp()
# run the window
label.run()
Output:

How to correctly use kivy graphics context instruction

Hi I recently tried my hands on kivy graphics, context instructions(rotate, ranslate etc). So i tried to implement an animated spinning wheel(used for example to show loading screen). I used garden.iconfonts package for this purpose following closely the example implemented in the package. Heres my code
.kv
<Convert>:
pos_hint: {'center_x':.5, 'center_y':.5}
size_hint: .8,.4
auto_dismiss: False
on_open:
self.load()
loading:loading
BoxLayout:
pos: root.pos
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
source: 'icons/olivine.png'
orientation: 'vertical'
Label: #STATUS 20 PERCENT
text: 'Converting file...'
id: status
size_hint: 1,.2
markup: True
RelativeLayout: #picture or rolling 60 %
size_hint: 1,.6
Label: #SPINNER
text: '{}'.format(icon('icon-spin6', 32))
size_hint: 1,1
markup: True
p: 0
id: loading
canvas:
PushMatrix
Rotate:
angle: -self.p
origin: self.center
axis: 0,0,1
PopMatrix
.py
from kivy.uix.modalview import ModalView
from kivy.properties import ObjectProperty
from kivy.animation import Animation
class Convert(ModalView):
loading= ObjectProperty()
def load(self):
anim = Animation(p = 360, duration= 1) + Animation(p=0 , duration=0)
anim.repeat = True
anim.start(self.loading)
From my code Convert is a popup that shows up when a button is clicked, then as it opens, shows the spinning wheel.
But when i run the code it just shows the wheel(i.e the iconfont), but does not spin.
The code only works when i change the canvas class under the Label, to canvas.before. I assume that my understanding of how to use these tools is still poor. So im hoping someone can help clearify what im doing wrong, and how to make this work using canvas
canvas:
PushMatrix
Rotate:
angle: -self.p
origin: self.center
axis: 0,0,1
PopMatrix
Everything between Rotate and PopMatrix will be rotated - that's the point of PushMatrix and PopMatrix, they bound the region where any matrix transformations are applied.
In this case, you didn't put anything in beween them, so you don't see anything rotated.
You probably want to put PushMatrix and Rotate in the canvas.before, and PopMatrix in the canvas.after. Since the Label drawing occurs in canvas, this will then be in the rotated state.

Categories